CodeWolf CodeWolf
333 Files
2584 Entities
0 Relationships

.editorconfig

.editorconfig

file: .editorconfig

# EditorConfig configuration for htop
# http://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file, utf-8 charset
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8

# match C source and header files, set indent to three spaces
[*.{c,h}]
indent_style = space
indent_size = 3
trim_trailing_whitespace = true

dependabot.yml

.github/dependabot.yml

file: dependabot.yml

---

version: 2

updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: weekly

FUNDING.yml

.github/FUNDING.yml

file: FUNDING.yml

custom: ["https://hcb.hackclub.com/donations/start/htop"]

build_release.yml

.github/workflows/build_release.yml

file: build_release.yml

name: Build Source Release

# Trigger whenever a release is created
on:
  release:
    types:
      - created

jobs:
  build:
    name: build
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
      with:
        submodules: true

    - name: archive
      id: archive
      run: |
        VERSION=${{ github.event.release.tag_name }}
        PKGNAME="htop-$VERSION"
        SHASUM=$PKGNAME.tar.xz.sha256
        autoreconf -i
        mkdir -p /tmp/$PKGNAME
        mv * /tmp/$PKGNAME
        mv /tmp/$PKGNAME .
        TARBALL=$PKGNAME.tar.xz
        tar cJf $TARBALL $PKGNAME
        sha256sum $TARBALL > $SHASUM
        echo "::set-output name=tarball::$TARBALL"
        echo "::set-output name=shasum::$SHASUM"
    - name: upload tarball
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ github.event.release.upload_url }}
        asset_path: ./${{ steps.archive.outputs.tarball }}
        asset_name: ${{ steps.archive.outputs.tarball }}
        asset_content_type: application/gzip

    - name: upload shasum
      uses: actions/upload-release-asset@v1
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        upload_url: ${{ github.event.release.upload_url }}
        asset_path: ./${{ steps.archive.outputs.shasum }}
        asset_name: ${{ steps.archive.outputs.shasum }}
        asset_content_type: text/plain

ci.yml

.github/workflows/ci.yml

file: ci.yml

codeql-analysis.yml

.github/workflows/codeql-analysis.yml

file: codeql-analysis.yml

name: "CodeQL"

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 1 * * 0'

permissions:
  contents: read

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      security-events: write

    env:
        # Enable format attributes in ncurses headers
        # Enable fortified memory/string handling
        CPPFLAGS: -DGCC_PRINTF -DGCC_SCANF -D_FORTIFY_SOURCE=2

    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Initialize CodeQL
      uses: github/codeql-action/init@v3
      with:
        languages: cpp

    - name: Install Dependencies
      run: sudo apt-get install --no-install-recommends libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors-dev libcap-dev

    - name: Bootstrap
      run: ./autogen.sh

    - name: Configure
      run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities

    - name: Build
      run: make

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v3

htoprc

.github/workflows/htoprc

file: htoprc

.gitignore

.gitignore

file: .gitignore

# the binaries:
htop
pcp-htop

# all object files
*.o

# skip all backups
*.bak
*~
.*.sw?

# skip coverage files
*.gcda
*/*.gcda
*.gcno
*/*.gcno
*.h.gch
*/.dirstamp

# automake/autoconf related files
.deps/
Makefile
Makefile.in
INSTALL
aclocal.m4
autom4te.cache/
compile
conf*/
config.guess
config.h
config.h.in
config.log
config.status
config.cache
config.sub
configure
depcomp
htop.1
pcp-htop.5
install-sh
libtool
ltmain.sh
m4/
missing
stamp-h1

# files related to valgrind/callgrind
callgrind.out.*

# IDE workspace configurations
/.idea/
/.vscode/

# Language Servers
/.cache/clangd/

.travis.yml

.travis.yml

file: .travis.yml

language: c

compiler:
  - clang
  - gcc

os:
  - freebsd

script:
  - ./autogen.sh
  - ./configure --enable-werror
  - make -k
  - make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
  - sudo make install
  - make installcheck

Action.c

Action.c

file: Action.c

Action.h

Action.h

enum: Htop_Reaction

typedef enum {
   HTOP_OK              = 0x00,
   HTOP_REFRESH         = 0x01,
   HTOP_RECALCULATE     = 0x02 | HTOP_REFRESH,
   HTOP_SAVE_SETTINGS   = 0x04,
   HTOP_KEEP_FOLLOWING  = 0x08,
   HTOP_QUIT            = 0x10,
   HTOP_REDRAW_BAR      = 0x20,
   HTOP_UPDATE_PANELHDR = 0x40 | HTOP_REFRESH,
   HTOP_RESIZE          = 0x80 | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR,
} Htop_Reaction;

function: Action_follow

Htop_Reaction Action_follow(State* st);

function: Action_pickFromVector

Object* Action_pickFromVector(State* st, Panel* list, int x, bool follow);

function: Action_setBindings

void Action_setBindings(Htop_Action* keys);

function: Action_setScreenTab

Htop_Reaction Action_setScreenTab(State* st, int x);

function: Action_setSortKey

Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey);

function: Action_setUserOnly

bool Action_setUserOnly(const char* userName, uid_t* userId);

function: State_hideFunctionBar

static inline bool State_hideFunctionBar(const State* st) {
   const Settings* settings = st->host->settings;
   return settings->hideFunctionBar == 2 || (settings->hideFunctionBar == 1 && st->hideSelection);
}

struct: MainPanel_

struct MainPanel_;

struct: State

typedef struct State_ {
   Machine* host;
   struct MainPanel_* mainPanel;
   Header* header;
   bool pauseUpdate;
   bool hideSelection;
   bool hideMeters;
} State;

typedef: Htop_Action

typedef Htop_Reaction (*Htop_Action)(State* st);

Affinity.c

Affinity.c

function: Affinity_add

void Affinity_add(Affinity* this, unsigned int id) {
   if (this->used == this->size) {
      this->size *= 2;
      this->cpus = xRealloc(this->cpus, sizeof(unsigned int) * this->size);
   }
   this->cpus[this->used] = id;
   this->used++;
}

function: Affinity_delete

void Affinity_delete(Affinity* this) {
   free(this->cpus);
   free(this);
}

function: Affinity_get (hwloc implementation)

static Affinity* Affinity_get(const Process* p, Machine* host) {
   hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
   bool ok = (hwloc_get_proc_cpubind(host->topology, Process_getPid(p), cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
   Affinity* affinity = NULL;
   if (ok) {
      affinity = Affinity_new(host);
      if (hwloc_bitmap_last(cpuset) == -1) {
         for (unsigned int i = 0; i < host->existingCPUs; i++) {
            Affinity_add(affinity, i);
         }
      } else {
         int id;
         hwloc_bitmap_foreach_begin(id, cpuset)
            Affinity_add(affinity, (unsigned)id);
         hwloc_bitmap_foreach_end();
      }
   }
   hwloc_bitmap_free(cpuset);
   return affinity;
}

function: Affinity_get (sched.h implementation)

static Affinity* Affinity_get(const Process* p, Machine* host) {
   cpu_set_t cpuset;
   bool ok = (sched_getaffinity(Process_getPid(p), sizeof(cpu_set_t), &cpuset) == 0);
   if (!ok)
      return NULL;

   Affinity* affinity = Affinity_new(host);
   for (unsigned int i = 0; i < host->existingCPUs; i++) {
      if (CPU_ISSET(i, &cpuset)) {
         Affinity_add(affinity, i);
      }
   }
   return affinity;
}

function: Affinity_new

Affinity* Affinity_new(Machine* host) {
   Affinity* this = xCalloc(1, sizeof(Affinity));
   this->size = 8;
   this->cpus = xCalloc(this->size, sizeof(unsigned int));
   this->host = host;
   return this;
}

function: Affinity_rowGet

Affinity* Affinity_rowGet(const Row* row, Machine* host) {
   const Process* p = (const Process*) row;
   assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
   return Affinity_get(p, host);
}

function: Affinity_rowSet

bool Affinity_rowSet(Row* row, Arg arg) {
   Process* p = (Process*) row;
   assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
   return Affinity_set(p, arg);
}

function: Affinity_set (hwloc implementation)

static bool Affinity_set(Process* p, Arg arg) {
   Affinity* this = arg.v;
   hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
   for (unsigned int i = 0; i < this->used; i++) {
      hwloc_bitmap_set(cpuset, this->cpus[i]);
   }
   bool ok = (hwloc_set_proc_cpubind(this->host->topology, Process_getPid(p), cpuset, HTOP_HWLOC_CPUBIND_FLAG) == 0);
   hwloc_bitmap_free(cpuset);
   return ok;
}

function: Affinity_set (sched.h implementation)

static bool Affinity_set(Process* p, Arg arg) {
   Affinity* this = arg.v;
   cpu_set_t cpuset;
   CPU_ZERO(&cpuset);
   for (unsigned int i = 0; i < this->used; i++) {
      CPU_SET(this->cpus[i], &cpuset);
   }
   bool ok = (sched_setaffinity(Process_getPid(p), sizeof(unsigned long), &cpuset) == 0);
   return ok;
}

Affinity.h

Affinity.h

function: Affinity_add

void Affinity_add(Affinity* this, unsigned int id);

function: Affinity_delete

void Affinity_delete(Affinity* this);

function: Affinity_new

Affinity* Affinity_new(Machine* host);

function: Affinity_rowGet

#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)

Affinity* Affinity_rowGet(const Row* row, Machine* host);

#endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */

function: Affinity_rowSet

#if defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY)

bool Affinity_rowSet(Row* row, Arg arg);

#endif /* HAVE_LIBHWLOC || HAVE_AFFINITY */

struct: Affinity_

typedef struct Affinity_ {
   Machine* host;
   unsigned int size;
   unsigned int used;
   unsigned int* cpus;
} Affinity;

AffinityPanel.c

AffinityPanel.c

array: AffinityPanelEvents

static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)};

array: AffinityPanelFunctions

static const char* const AffinityPanelFunctions[] = {
   "Set    ",
   "Cancel ",
   #ifdef HAVE_LIBHWLOC
   "All",
   "Topology",
   "               ",
   #endif
   NULL
};

array: AffinityPanelKeys

static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"};

class: AffinityPanel_class

const PanelClass AffinityPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = AffinityPanel_delete
   },
   .eventHandler = AffinityPanel_eventHandler
};

class: MaskItem_class

static const ObjectClass MaskItem_class = {
   .display = MaskItem_display,
   .delete  = MaskItem_delete
};

function: AffinityPanel_addObject

function: AffinityPanel_buildTopology

#ifdef HAVE_LIBHWLOC

static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
   MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
   if (obj->next_sibling) {
      indent |= (1U << obj->depth);
   } else {
      indent &= ~(1U << obj->depth);
   }

   for (unsigned i = 0; i < obj->arity; i++) {
      AffinityPanel_buildTopology(this, obj->children[i], indent, item);
   }

   return parent == NULL ? item : NULL;
}

#endif

function: AffinityPanel_delete

static void AffinityPanel_delete(Object* cast) {
   AffinityPanel* this = (AffinityPanel*) cast;
   Vector_delete(this->cpuids);
   #ifdef HAVE_LIBHWLOC
   hwloc_bitmap_free(this->workCpuset);
   MaskItem_delete((Object*) this->topoRoot);
   #endif
   Panel_done(&this->super);
   free(this);
}

function: AffinityPanel_eventHandler

static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
   AffinityPanel* this = (AffinityPanel*) super;

   HandlerResult result = IGNORED;
   MaskItem* selected = (MaskItem*) Panel_getSelected(super);

   bool keepSelected = true;

   switch (ch) {
      case KEY_MOUSE:
      case KEY_RECLICK:
      case ' ':
         if (!selected) {
            return result;
         }

         #ifdef HAVE_LIBHWLOC
         if (selected->value == 2) {
            /* Item was selected, so remove this mask from the top cpuset. */
            hwloc_bitmap_andnot(this->workCpuset, this->workCpuset, selected->cpuset);
            selected->value = 0;
         } else {
            /* Item was not or only partial selected, so set all bits from this object
               in the top cpuset. */
            hwloc_bitmap_or(this->workCpuset, this->workCpuset, selected->cpuset);
            selected->value = 2;
         }
         #else
         selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */
         #endif

         result = HANDLED;
         break;

#ifdef HAVE_LIBHWLOC

      case KEY_F(1):
         hwloc_bitmap_copy(this->workCpuset, this->allCpuset);
         result = HANDLED;
         break;

      case KEY_F(2):
         this->topoView = !this->topoView;
         keepSelected = false;

         result = HANDLED;
         break;

      case KEY_F(3):
      case '-':
      case '+':
         if (!selected) {
            break;
         }

         if (selected->sub_tree) {
            selected->sub_tree = 1 + !(selected->sub_tree - 1); /* toggle between 1 and 2 */
         }

         result = HANDLED;
         break;

#endif

      case 0x0a:
      case 0x0d:
      case KEY_ENTER:
         result = BREAK_LOOP;
         break;
   }

   if (HANDLED == result)
      AffinityPanel_update(this, keepSelected);

   return result;
}

function: AffinityPanel_getAffinity

Affinity* AffinityPanel_getAffinity(Panel* super, Machine* host) {
   const AffinityPanel* this = (AffinityPanel*) super;
   Affinity* affinity = Affinity_new(host);

   #ifdef HAVE_LIBHWLOC
   int i;
   hwloc_bitmap_foreach_begin(i, this->workCpuset)
      Affinity_add(affinity, (unsigned)i);
   hwloc_bitmap_foreach_end();
   #else
   for (int i = 0; i < Vector_size(this->cpuids); i++) {
      const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i);
      if (item->value) {
         Affinity_add(affinity, item->cpu);
      }
   }
   #endif

   return affinity;
}

function: AffinityPanel_new

Panel* AffinityPanel_new(Machine* host, const Affinity* affinity, int* width) {
   AffinityPanel* this = AllocThis(AffinityPanel);
   Panel* super = &this->super;

   Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents));

   this->host = host;
   /* defaults to 15, this also includes the gap between the panels,
    * but this will be added by the caller */
   this->width = 14;

   this->cpuids   = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);

   #ifdef HAVE_LIBHWLOC
   this->topoView = host->settings->topologyAffinity;
   #else
   this->topoView = false;
   #endif

   #ifdef HAVE_LIBHWLOC
   this->allCpuset  = hwloc_topology_get_complete_cpuset(host->topology);
   this->workCpuset = hwloc_bitmap_alloc();
   #endif

   Panel_setHeader(super, "Use CPUs:");

   unsigned int curCpu = 0;
   for (unsigned int i = 0; i < host->existingCPUs; i++) {
      if (!Machine_isCPUonline(host, i))
         continue;

      char number[16];
      xSnprintf(number, 9, "CPU %d", Settings_cpuId(host->settings, i));
      unsigned cpu_width = 4 + strlen(number);
      if (cpu_width > this->width) {
         this->width = cpu_width;
      }

      bool isSet = false;
      if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
         #ifdef HAVE_LIBHWLOC
         hwloc_bitmap_set(this->workCpuset, i);
         #endif
         isSet = true;
         curCpu++;
      }

      MaskItem* cpuItem = MaskItem_newSingleton(number, i, isSet);
      Vector_add(this->cpuids, (Object*) cpuItem);
   }

   #ifdef HAVE_LIBHWLOC
   this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(host->topology), 0, NULL);
   #endif

   if (width) {
      *width = this->width;
   }

   AffinityPanel_update(this, false);

   return super;
}

function: AffinityPanel_update

static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
   Panel* super = &this->super;

   FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");

   int oldSelected = Panel_getSelectedIndex(super);
   Panel_prune(super);

   #ifdef HAVE_LIBHWLOC
   if (this->topoView) {
      AffinityPanel_updateTopo(this, this->topoRoot);
   } else {
      for (int i = 0; i < Vector_size(this->cpuids); i++) {
         AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
      }
   }
   #else
   Panel_splice(super, this->cpuids);
   #endif

   if (keepSelected)
      Panel_setSelected(super, oldSelected);

   super->needsRedraw = true;
}

function: AffinityPanel_updateItem

#ifdef HAVE_LIBHWLOC

static void AffinityPanel_updateItem(AffinityPanel* this, MaskItem* item) {
   Panel* super = &this->super;

   item->value = hwloc_bitmap_isincluded(item->cpuset, this->workCpuset) ? 2 :
                 hwloc_bitmap_intersects(item->cpuset, this->workCpuset) ? 1 : 0;

   Panel_add(super, (Object*) item);
}

#endif

function: AffinityPanel_updateTopo

#ifdef HAVE_LIBHWLOC

static void AffinityPanel_updateTopo(AffinityPanel* this, MaskItem* item) {
   AffinityPanel_updateItem(this, item);

   if (item->sub_tree == 2)
      return;

   for (int i = 0; i < Vector_size(item->children); i++)
      AffinityPanel_updateTopo(this, (MaskItem*) Vector_get(item->children, i));
}

#endif

function: MaskItem_delete

static void MaskItem_delete(Object* cast) {
   MaskItem* this = (MaskItem*) cast;
   free(this->text);
   free(this->indent);
   Vector_delete(this->children);
   #ifdef HAVE_LIBHWLOC
   if (this->ownCpuset)
      hwloc_bitmap_free(this->cpuset);
   #endif
   free(this);
}

function: MaskItem_display

static void MaskItem_display(const Object* cast, RichString* out) {
   const MaskItem* this = (const MaskItem*)cast;
   assert (this != NULL);
   RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
   if (this->value == 2) {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
   } else if (this->value == 1) {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
   } else {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
   }
   RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
   RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
   if (this->indent) {
      RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
      RichString_appendWide(out, CRT_colors[PROCESS_TREE],
                            this->sub_tree == 2
                            ? CRT_treeStr[TREE_STR_OPEN]
                            : CRT_treeStr[TREE_STR_SHUT]);
      RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
   }
   RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
}

function: MaskItem_newMask

#ifdef HAVE_LIBHWLOC

static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bitmap_t cpuset, bool owner) {
   MaskItem* this = AllocThis(MaskItem);
   this->text = xStrdup(text);
   this->indent = xStrdup(indent); /* nonnull for tree node */
   this->value = 0;
   this->ownCpuset = owner;
   this->cpuset = cpuset;
   this->sub_tree = hwloc_bitmap_weight(cpuset) > 1 ? 1 : 0;
   this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
   return this;
}

#endif

function: MaskItem_newSingleton

static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
   MaskItem* this = AllocThis(MaskItem);
   this->text = xStrdup(text);
   this->indent = NULL; /* not a tree node */
   this->sub_tree = 0;
   this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);

   #ifdef HAVE_LIBHWLOC
   this->ownCpuset = true;
   this->cpuset = hwloc_bitmap_alloc();
   hwloc_bitmap_set(this->cpuset, cpu);
   #else
   this->cpu = cpu;
   #endif
   this->value = isSet ? 2 : 0;

   return this;
}

struct: AffinityPanel_

typedef struct AffinityPanel_ {
   Panel super;
   Machine* host;
   bool topoView;
   Vector* cpuids;
   unsigned width;

   #ifdef HAVE_LIBHWLOC
   MaskItem* topoRoot;
   hwloc_const_cpuset_t allCpuset;
   hwloc_bitmap_t workCpuset;
   #endif
} AffinityPanel;

struct: MaskItem_

typedef struct MaskItem_ {
   Object super;
   char* text;
   char* indent; /* used also as an condition whether this is a tree node */
   int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */
   int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */
   Vector* children;
   #ifdef HAVE_LIBHWLOC
   bool ownCpuset;
   hwloc_bitmap_t cpuset;
   #else
   int cpu;
   #endif
} MaskItem;

AffinityPanel.h

AffinityPanel.h

function declaration: AffinityPanel_getAffinity

Affinity* AffinityPanel_getAffinity(Panel* super, Machine* host);

function declaration: AffinityPanel_new

Panel* AffinityPanel_new(Machine* host, const Affinity* affinity, int* width);

global constant declaration: AffinityPanel_class

extern const PanelClass AffinityPanel_class;

AUTHORS

AUTHORS

file: AUTHORS

Originally authored by:
 Hisham H. Muhammad

Currently maintained by the htop dev team:
 Benny Baumann
 Christian Göttsche
 Daniel Lange
 Nathan Scott

For the full list of contributors see:
 git log --format="%aN" | sort -u

autogen.sh

autogen.sh

script: autogen.sh

#!/bin/sh
autoreconf --force --install --verbose -Wall

AvailableColumnsPanel.c

AvailableColumnsPanel.c

data structure: AvailableColumnsFunctions

static const char* const AvailableColumnsFunctions[] = {"      ", "      ", "      ", "      ", "Add   ", "      ", "      ", "      ", "      ", "Done  ", NULL};

function: AvailableColumnsPanel_addDynamicColumn

static void AvailableColumnsPanel_addDynamicColumn(ht_key_t key, void* value, void* data) {
   const DynamicColumn* column = (const DynamicColumn*) value;
   if (column->table) /* DynamicScreen, handled differently */
      return;
   AvailableColumnsPanel* this = (AvailableColumnsPanel*) data;
   const char* title = column->heading ? column->heading : column->name;
   const char* text = column->description ? column->description : column->caption;
   char description[256];
   if (text)
      xSnprintf(description, sizeof(description), "%s - %s", title, text);
   else
      xSnprintf(description, sizeof(description), "%s", title);
   Panel_add(&this->super, (Object*) ListItem_new(description, key));
}

function: AvailableColumnsPanel_addDynamicColumns

static void AvailableColumnsPanel_addDynamicColumns(AvailableColumnsPanel* this, Hashtable* dynamicColumns) {
   assert(dynamicColumns);
   Hashtable_foreach(dynamicColumns, AvailableColumnsPanel_addDynamicColumn, this);
}

function: AvailableColumnsPanel_addDynamicScreens

static void AvailableColumnsPanel_addDynamicScreens(AvailableColumnsPanel* this, const char* screen) {
   Platform_addDynamicScreenAvailableColumns(&this->super, screen);
}

function: AvailableColumnsPanel_addPlatformColumns

static void AvailableColumnsPanel_addPlatformColumns(AvailableColumnsPanel* this) {
   for (int i = 1; i < LAST_PROCESSFIELD; i++) {
      if (Process_fields[i].description) {
         char description[256];
         xSnprintf(description, sizeof(description), "%s - %s", Process_fields[i].name, Process_fields[i].description);
         Panel_add(&this->super, (Object*) ListItem_new(description, i));
      }
   }
}

function: AvailableColumnsPanel_delete

static void AvailableColumnsPanel_delete(Object* object) {
   AvailableColumnsPanel* this = (AvailableColumnsPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: AvailableColumnsPanel_eventHandler

static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
   AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
   HandlerResult result = IGNORED;

   switch (ch) {
      case 13:
      case KEY_ENTER:
      case KEY_F(5): {
         const ListItem* selected = (ListItem*) Panel_getSelected(super);
         if (!selected)
            break;

         int at = Panel_getSelectedIndex(this->columns);
         AvailableColumnsPanel_insert(this, at, selected->key);
         Panel_setSelected(this->columns, at + 1);
         ColumnsPanel_update(this->columns);
         result = HANDLED;
         break;
      }
      default:
         if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
            result = Panel_selectByTyping(super, ch);
         break;
   }
   return result;
}

function: AvailableColumnsPanel_fill

void AvailableColumnsPanel_fill(AvailableColumnsPanel* this, const char* dynamicScreen, Hashtable* dynamicColumns) {
   Panel_prune(&this->super);
   if (dynamicScreen) {
      AvailableColumnsPanel_addDynamicScreens(this, dynamicScreen);
   } else {
      AvailableColumnsPanel_addPlatformColumns(this);
      AvailableColumnsPanel_addDynamicColumns(this, dynamicColumns);
   }
}

function: AvailableColumnsPanel_insert

static void AvailableColumnsPanel_insert(AvailableColumnsPanel* this, int at, int key) {
   const char* name;
   if (key >= ROW_DYNAMIC_FIELDS)
      name = DynamicColumn_name(key);
   else
      name = Process_fields[key].name;
   Panel_insert(this->columns, at, (Object*) ListItem_new(name, key));
}

function: AvailableColumnsPanel_new

AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns) {
   AvailableColumnsPanel* this = AllocThis(AvailableColumnsPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(AvailableColumnsFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
   Panel_setHeader(super, "Available Columns");

   this->columns = columns;
   AvailableColumnsPanel_fill(this, NULL, dynamicColumns);

   return this;
}

struct: AvailableColumnsPanel

/* Implied structure based on usage in AvailableColumnsPanel.c */
typedef struct AvailableColumnsPanel_ { 
   Panel super; /* Inherits from Panel */
   Panel* columns; /* Pointer to the target ColumnsPanel where selected columns are added */
} AvailableColumnsPanel;

struct instance: AvailableColumnsPanel_class

const PanelClass AvailableColumnsPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = AvailableColumnsPanel_delete
   },
   .eventHandler = AvailableColumnsPanel_eventHandler
};

AvailableColumnsPanel.h

AvailableColumnsPanel.h

extern const: AvailableColumnsPanel_class

extern const PanelClass AvailableColumnsPanel_class;

function: AvailableColumnsPanel_fill

void AvailableColumnsPanel_fill(AvailableColumnsPanel* this, const char* dynamicScreen, Hashtable* dynamicColumns);

function: AvailableColumnsPanel_new

AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns, Hashtable* dynamicColumns);

struct: AvailableColumnsPanel_

typedef struct AvailableColumnsPanel_ {
   Panel super;
   Panel* columns;
} AvailableColumnsPanel;

AvailableMetersPanel.c

AvailableMetersPanel.c

function: AvailableMetersPanel_addCPUMeters

static void AvailableMetersPanel_addCPUMeters(Panel* super, const MeterClass* type, const Machine* host) {
   if (host->existingCPUs > 1) {
      Panel_add(super, (Object*) ListItem_new("CPU average", 0));
      for (unsigned int i = 1; i <= host->existingCPUs; i++) {
         char buffer[50];
         xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(host->settings, i - 1));
         Panel_add(super, (Object*) ListItem_new(buffer, i));
      }
   } else {
      Panel_add(super, (Object*) ListItem_new(type->uiName, 1));
   }
}

function: AvailableMetersPanel_addDynamicMeter

static void AvailableMetersPanel_addDynamicMeter(ATTR_UNUSED ht_key_t key, void* value, void* data) {
   const DynamicMeter* meter = (const DynamicMeter*)value;
   DynamicIterator* iter = (DynamicIterator*)data;
   unsigned int identifier = (iter->offset << 16) | iter->id;
   const char* label = meter->description ? meter->description : meter->caption;
   if (!label)
      label = meter->name; /* last fallback to name, guaranteed set */
   Panel_add(iter->super, (Object*) ListItem_new(label, identifier));
   iter->id++;
}

function: AvailableMetersPanel_addDynamicMeters

static void AvailableMetersPanel_addDynamicMeters(Panel* super, const Settings* settings, unsigned int offset) {
   DynamicIterator iter = { .super = super, .id = 1, .offset = offset };
   Hashtable* dynamicMeters = settings->dynamicMeters;
   assert(dynamicMeters != NULL);
   Hashtable_foreach(dynamicMeters, AvailableMetersPanel_addDynamicMeter, &iter);
}

function: AvailableMetersPanel_addMeter

static inline void AvailableMetersPanel_addMeter(Header* header, MetersPanel* panel, const MeterClass* type, unsigned int param, size_t column) {
   const Meter* meter = Header_addMeterByClass(header, type, param, column);
   Panel_add((Panel*)panel, (Object*) Meter_toListItem(meter, false));
   Panel_setSelected((Panel*)panel, Panel_size((Panel*)panel) - 1);
   MetersPanel_setMoving(panel, true);
}

function: AvailableMetersPanel_addPlatformMeter

static void AvailableMetersPanel_addPlatformMeter(Panel* super, const MeterClass* type, unsigned int offset) {
   const char* label = type->description ? type->description : type->uiName;
   Panel_add(super, (Object*) ListItem_new(label, offset << 16));
}

function: AvailableMetersPanel_delete

static void AvailableMetersPanel_delete(Object* object) {
   AvailableMetersPanel* this = (AvailableMetersPanel*) object;
   free(this->meterPanels);
   Panel_done(&this->super);
   free(this);
}

function: AvailableMetersPanel_eventHandler

static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
   AvailableMetersPanel* this = (AvailableMetersPanel*) super;
   Header* header = this->header;

   const ListItem* selected = (ListItem*) Panel_getSelected(super);
   if (!selected)
      return IGNORED;

   unsigned int param = selected->key & 0xffff;
   int type = selected->key >> 16;
   HandlerResult result = IGNORED;
   bool update = false;

   switch (ch) {
      case KEY_F(5):
      case 'l':
      case 'L':
         AvailableMetersPanel_addMeter(header, this->meterPanels[0], Platform_meterTypes[type], param, 0);
         result = HANDLED;
         update = true;
         break;
      case 0x0a:
      case 0x0d:
      case KEY_ENTER:
      case KEY_F(6):
      case 'r':
      case 'R':
         AvailableMetersPanel_addMeter(header, this->meterPanels[this->columns - 1], Platform_meterTypes[type], param, this->columns - 1);
         result = (KEY_LEFT << 16) | SYNTH_KEY;
         update = true;
         break;
   }

   if (update) {
      Settings* settings = this->host->settings;
      settings->changed = true;
      settings->lastUpdate++;
      Header_calculateHeight(header);
      Header_updateData(header);
      Header_draw(header);
      ScreenManager_resize(this->scr);
   }

   return result;
}

function: AvailableMetersPanel_new

AvailableMetersPanel* AvailableMetersPanel_new(Machine* host, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr) {
   AvailableMetersPanel* this = AllocThis(AvailableMetersPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_newEnterEsc("Add   ", "Done   ");
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   this->host = host;
   this->header = header;
   this->columns = columns;
   this->meterPanels = meterPanels;
   this->scr = scr;

   Panel_setHeader(super, "Available meters");
   // Platform_meterTypes[0] should be always (&CPUMeter_class) which we will
   // handle separately in the code below.  Likewise, identifiers for Dynamic
   // Meters are handled separately - similar to CPUs, this allows generation
   // of multiple different Meters (also using 'param' to distinguish them).
   for (unsigned int i = 1; Platform_meterTypes[i]; i++) {
      const MeterClass* type = Platform_meterTypes[i];
      assert(type != &CPUMeter_class);
      if (type == &DynamicMeter_class)
         AvailableMetersPanel_addDynamicMeters(super, host->settings, i);
      else
         AvailableMetersPanel_addPlatformMeter(super, type, i);
   }
   AvailableMetersPanel_addCPUMeters(super, &CPUMeter_class, host);

   return this;
}

struct: AvailableMetersPanel_class

const PanelClass AvailableMetersPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = AvailableMetersPanel_delete
   },
   .eventHandler = AvailableMetersPanel_eventHandler
};

struct: DynamicIterator

typedef struct {
   Panel* super;
   unsigned int id;
   unsigned int offset;
} DynamicIterator;

AvailableMetersPanel.h

AvailableMetersPanel.h

function: AvailableMetersPanel_new

AvailableMetersPanel* AvailableMetersPanel_new(Machine* host, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr);

struct: AvailableMetersPanel_

typedef struct AvailableMetersPanel_ {
   Panel super;
   ScreenManager* scr;
   Machine* host;
   Header* header;
   size_t columns;
   MetersPanel** meterPanels;
} AvailableMetersPanel;

variable: AvailableMetersPanel_class

extern const PanelClass AvailableMetersPanel_class;

BatteryMeter.c

BatteryMeter.c

function: BatteryMeter_updateValues

static void BatteryMeter_updateValues(Meter* this) {
   ACPresence isOnAC;
   double percent;

   Platform_getBattery(&percent, &isOnAC);

   if (!isNonnegative(percent)) {
      this->values[0] = NAN;
      xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A");
      return;
   }

   this->values[0] = percent;

   const char* text;
   switch (isOnAC) {
      case AC_PRESENT:
         text = this->mode == TEXT_METERMODE ? " (Running on A/C)" : "(A/C)";
         break;
      case AC_ABSENT:
         text = this->mode == TEXT_METERMODE ? " (Running on battery)" : "(bat)";
         break;
      case AC_ERROR:
      default:
         text = "";
         break;
   }

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%%s", percent, text);
}

struct: BatteryMeter_class

const MeterClass BatteryMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = BatteryMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 1,
   .total = 100.0,
   .attributes = BatteryMeter_attributes,
   .name = "Battery",
   .uiName = "Battery",
   .caption = "Battery: "
};

variable: BatteryMeter_attributes

static const int BatteryMeter_attributes[] = {
   BATTERY
};

BatteryMeter.h

BatteryMeter.h

struct: ACPresence

typedef enum ACPresence_ {
   AC_ABSENT,
   AC_PRESENT,
   AC_ERROR
} ACPresence;

struct: BatteryMeter_class

extern const MeterClass BatteryMeter_class;

CategoriesPanel.c

CategoriesPanel.c

function: CategoriesPanel_delete

static void CategoriesPanel_delete(Object* object) {
   CategoriesPanel* this = (CategoriesPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: CategoriesPanel_eventHandler

static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
   CategoriesPanel* this = (CategoriesPanel*) super;

   HandlerResult result = IGNORED;

   int selected = Panel_getSelectedIndex(super);
   switch (ch) {
      case EVENT_SET_SELECTED:
         result = HANDLED;
         break;
      case KEY_UP:
      case KEY_CTRL('P'):
      case KEY_DOWN:
      case KEY_CTRL('N'):
      case KEY_NPAGE:
      case KEY_PPAGE:
      case KEY_HOME:
      case KEY_END: {
         int previous = selected;
         Panel_onKey(super, ch);
         selected = Panel_getSelectedIndex(super);
         if (previous != selected)
            result = HANDLED;
         break;
      }
      default:
         if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
            result = Panel_selectByTyping(super, ch);
         if (result == BREAK_LOOP)
            result = IGNORED;
         break;
   }
   if (result == HANDLED) {
      int size = ScreenManager_size(this->scr);
      for (int i = 1; i < size; i++)
         ScreenManager_remove(this->scr, 1);

      if (selected >= 0 && (size_t)selected < ARRAYSIZE(categoriesPanelPages)) {
         categoriesPanelPages[selected].ctor(this);
      }
   }
   return result;
}

function: CategoriesPanel_makeColorsPage

static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
   Settings* settings = this->host->settings;
   Panel* colors = (Panel*) ColorsPanel_new(settings);
   ScreenManager_add(this->scr, colors, -1);
}

function: CategoriesPanel_makeDisplayOptionsPage

static void CategoriesPanel_makeDisplayOptionsPage(CategoriesPanel* this) {
   Settings* settings = this->host->settings;
   Panel* displayOptions = (Panel*) DisplayOptionsPanel_new(settings, this->scr);
   ScreenManager_add(this->scr, displayOptions, -1);
}

function: CategoriesPanel_makeHeaderOptionsPage

static void CategoriesPanel_makeHeaderOptionsPage(CategoriesPanel* this) {
   Settings* settings = this->host->settings;
   Panel* colors = (Panel*) HeaderOptionsPanel_new(settings, this->scr);
   ScreenManager_add(this->scr, colors, -1);
}

function: CategoriesPanel_makeMetersPage

static void CategoriesPanel_makeMetersPage(CategoriesPanel* this) {
   size_t columns = HeaderLayout_getColumns(this->scr->header->headerLayout);
   MetersPanel** meterPanels = xMallocArray(columns, sizeof(MetersPanel*));
   Settings* settings = this->host->settings;

   for (size_t i = 0; i < columns; i++) {
      char titleBuffer[32];
      xSnprintf(titleBuffer, sizeof(titleBuffer), "Column %zu", i + 1);
      meterPanels[i] = MetersPanel_new(settings, titleBuffer, this->header->columns[i], this->scr);

      if (i != 0) {
         meterPanels[i]->leftNeighbor = meterPanels[i - 1];
         meterPanels[i - 1]->rightNeighbor = meterPanels[i];
      }

      ScreenManager_add(this->scr, (Panel*) meterPanels[i], 20);
   }

   Panel* availableMeters = (Panel*) AvailableMetersPanel_new(this->host, this->header, columns, meterPanels, this->scr);
   ScreenManager_add(this->scr, availableMeters, -1);
}

function: CategoriesPanel_makeScreensPage

static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
   Settings* settings = this->host->settings;
   Panel* screens = (Panel*) ScreensPanel_new(settings);
   Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
   Panel* availableColumns = (Panel*) ((ScreensPanel*)screens)->availableColumns;
   ScreenManager_add(this->scr, screens, 20);
   ScreenManager_add(this->scr, columns, 20);
   ScreenManager_add(this->scr, availableColumns, -1);
}

function: CategoriesPanel_makeScreenTabsPage

#if defined(HTOP_PCP)   /* all platforms supporting dynamic screens */
static void CategoriesPanel_makeScreenTabsPage(CategoriesPanel* this) {
   Settings* settings = this->host->settings;
   Panel* screenTabs = (Panel*) ScreenTabsPanel_new(settings);
   Panel* screenNames = (Panel*) ((ScreenTabsPanel*)screenTabs)->names;
   ScreenManager_add(this->scr, screenTabs, 20);
   ScreenManager_add(this->scr, screenNames, -1);
}
#endif

function: CategoriesPanel_new

CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Header* header, Machine* host) {
   CategoriesPanel* this = AllocThis(CategoriesPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(CategoriesFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   this->scr = scr;
   this->host = host;
   this->header = header;
   Panel_setHeader(super, "Categories");
   for (size_t i = 0; i < ARRAYSIZE(categoriesPanelPages); i++)
      Panel_add(super, (Object*) ListItem_new(categoriesPanelPages[i].name, 0));

   ScreenManager_add(scr, super, 16);
   categoriesPanelPages[0].ctor(this);
   return this;
}

global constant array: CategoriesFunctions

static const char* const CategoriesFunctions[] = {"      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "Done  ", NULL};

global constant array: categoriesPanelPages

static CategoriesPanelPage categoriesPanelPages[] = {
   { .name = "Display options", .ctor = CategoriesPanel_makeDisplayOptionsPage },
   { .name = "Header layout", .ctor = CategoriesPanel_makeHeaderOptionsPage },
   { .name = "Meters", .ctor = CategoriesPanel_makeMetersPage },
#if defined(HTOP_PCP)   /* all platforms supporting dynamic screens */
   { .name = "Screen tabs", .ctor = CategoriesPanel_makeScreenTabsPage },
#endif
   { .name = "Screens", .ctor = CategoriesPanel_makeScreensPage },
   { .name = "Colors", .ctor = CategoriesPanel_makeColorsPage },
};

struct: CategoriesPanelPage

typedef struct CategoriesPanelPage_ {
   const char* name;
   CategoriesPanel_makePageFunc ctor;
} CategoriesPanelPage;

struct instance: CategoriesPanel_class

const PanelClass CategoriesPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = CategoriesPanel_delete
   },
   .eventHandler = CategoriesPanel_eventHandler
};

typedef: CategoriesPanel_makePageFunc

typedef void (* CategoriesPanel_makePageFunc)(CategoriesPanel* ref);

CategoriesPanel.h

CategoriesPanel.h

function: CategoriesPanel_new

CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Header* header, Machine* host);

struct: CategoriesPanel_

typedef struct CategoriesPanel_ {
   Panel super;
   ScreenManager* scr;
   Machine* host;
   Header* header;
} CategoriesPanel;

struct: CategoriesPanel_class

extern const PanelClass CategoriesPanel_class;

ChangeLog

ChangeLog

file: ChangeLog

check-pcp-style.sh

check-pcp-style.sh

function: check_file

method: check_section

function check_section() {
   missing = ""
   for (i in required_names) {
      rname = required_names[i]
      if (rname ~ /\?$/) {
         continue
      }
      if (rname ~ /^\*\./) {
         rname = substr(rname, 3, length(rname) - 2)
         for (g in groups) {
            if (g == "") {
               continue
            }
            if (!(g rname in seen)) {
               missing = missing g rname " "
            }
         }
         continue
      }
      if (!(rname in seen)) {
         missing = missing rname " "
      }
   }
   if (missing != "") {
      print "Error: Missing " missing "in section " section
      exit 1
   }
}

method: trim

function trim(s) {
   gsub(/^[ \t]+|[ \t]+$/, "", s)
   return s
}

structure: Awk Validation Script

ClockMeter.c

ClockMeter.c

function: ClockMeter_updateValues

static void ClockMeter_updateValues(Meter* this) {
   const Machine* host = this->host;

   struct tm result;
   const struct tm* lt = localtime_r(&host->realtime.tv_sec, &result);
   strftime(this->txtBuffer, sizeof(this->txtBuffer), "%H:%M:%S", lt);
}

global variable: ClockMeter_attributes

static const int ClockMeter_attributes[] = {
   CLOCK
};

struct instance (class definition): ClockMeter_class

const MeterClass ClockMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = ClockMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE) | (1 << LED_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = ClockMeter_attributes,
   .name = "Clock",
   .uiName = "Clock",
   .caption = "Time: ",
};

ClockMeter.h

ClockMeter.h

variable: ClockMeter_class

extern const MeterClass ClockMeter_class;

ColorsPanel.c

ColorsPanel.c

function: ColorsPanel_delete

static void ColorsPanel_delete(Object* object) {
   ColorsPanel* this = (ColorsPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: ColorsPanel_eventHandler

static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
   ColorsPanel* this = (ColorsPanel*) super;

   HandlerResult result = IGNORED;

   switch (ch) {
      case 0x0a:
      case 0x0d:
      case KEY_ENTER:
      case KEY_MOUSE:
      case KEY_RECLICK:
      case ' ': {
         int mark = Panel_getSelectedIndex(super);
         assert(mark >= 0);
         assert(mark < LAST_COLORSCHEME);

         for (int i = 0; ColorSchemeNames[i] != NULL; i++)
            CheckItem_set((CheckItem*)Panel_get(super, i), false);
         CheckItem_set((CheckItem*)Panel_get(super, mark), true);

         this->settings->colorScheme = mark;
         this->settings->changed = true;
         this->settings->lastUpdate++;

         CRT_setColors(mark);
         clear();

         result = HANDLED | REDRAW;
      }
   }

   return result;
}

function: ColorsPanel_new

ColorsPanel* ColorsPanel_new(Settings* settings) {
   ColorsPanel* this = AllocThis(ColorsPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(ColorsFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);

   this->settings = settings;

   assert(ARRAYSIZE(ColorSchemeNames) == LAST_COLORSCHEME + 1);

   Panel_setHeader(super, "Colors");
   for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
      Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));
   }
   CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true);
   return this;
}

struct instance: ColorsPanel_class

const PanelClass ColorsPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = ColorsPanel_delete
   },
   .eventHandler = ColorsPanel_eventHandler
};

ColorsPanel.h

ColorsPanel.h

function: ColorsPanel_new

ColorsPanel* ColorsPanel_new(Settings* settings);

struct: ColorsPanel

typedef struct ColorsPanel_ {
   Panel super;

   Settings* settings;
} ColorsPanel;

variable: ColorsPanel_class

extern const PanelClass ColorsPanel_class;

ColumnsPanel.c

ColumnsPanel.c

class: ColumnsPanel_class

const PanelClass ColumnsPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = ColumnsPanel_delete
   },
   .eventHandler = ColumnsPanel_eventHandler
};

function: ColumnsPanel_add

static void ColumnsPanel_add(Panel* super, unsigned int key, Hashtable* columns) {
   const char* name;
   if (key < LAST_PROCESSFIELD) {
      name = Process_fields[key].name;
   } else {
      const DynamicColumn* column = Hashtable_get(columns, key);
      assert(column);
      if (!column) {
         name = NULL;
      } else {
         /* heading preferred here but name is always available */
         name = column->heading ? column->heading : column->name;
      }
   }
   if (name == NULL)
      name = "- ";
   Panel_add(super, (Object*) ListItem_new(name, key));
}

function: ColumnsPanel_delete

static void ColumnsPanel_delete(Object* object) {
   ColumnsPanel* this = (ColumnsPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: ColumnsPanel_eventHandler

static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
   ColumnsPanel* const this = (ColumnsPanel*) super;

   int selected = Panel_getSelectedIndex(super);
   HandlerResult result = IGNORED;
   int size = Panel_size(super);

   switch (ch) {
      case 0x0a:
      case 0x0d:
      case KEY_ENTER:
      case KEY_MOUSE:
      case KEY_RECLICK:
         if (selected < size) {
            this->moving = !(this->moving);
            Panel_setSelectionColor(super, this->moving ? PANEL_SELECTION_FOLLOW : PANEL_SELECTION_FOCUS);
            ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
            if (selectedItem)
               selectedItem->moving = this->moving;
            result = HANDLED;
         }
         break;
      case KEY_UP:
         if (!this->moving)
            break;
         /* else fallthrough */
      case KEY_F(7):
      case '[':
      case '-':
         if (selected < size)
            Panel_moveSelectedUp(super);
         result = HANDLED;
         break;
      case KEY_DOWN:
         if (!this->moving)
            break;
         /* else fallthrough */
      case KEY_F(8):
      case ']':
      case '+':
         if (selected < size - 1)
            Panel_moveSelectedDown(super);
         result = HANDLED;
         break;
      case KEY_F(9):
      case KEY_DC:
      case KEY_DEL_MAC:
         if (size > 1 && selected < size)
            Panel_remove(super, selected);
         result = HANDLED;
         break;
      default:
         if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
            result = Panel_selectByTyping(super, ch);
         if (result == BREAK_LOOP)
            result = IGNORED;
         break;
   }

   if (result == HANDLED)
      ColumnsPanel_update(super);

   return result;
}

function: ColumnsPanel_fill

void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns) {
   Panel* super = &this->super;
   Panel_prune(super);
   for (const RowField* fields = ss->fields; *fields; fields++)
      ColumnsPanel_add(super, *fields, columns);
   this->ss = ss;
}

function: ColumnsPanel_new

ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed) {
   ColumnsPanel* this = AllocThis(ColumnsPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   this->ss = ss;
   this->changed = changed;
   this->moving = false;
   Panel_setHeader(super, "Active Columns");

   ColumnsPanel_fill(this, ss, columns);

   return this;
}

function: ColumnsPanel_update

void ColumnsPanel_update(Panel* super) {
   ColumnsPanel* this = (ColumnsPanel*) super;
   int size = Panel_size(super);
   *(this->changed) = true;
   this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size + 1));
   this->ss->flags = 0;
   for (int i = 0; i < size; i++) {
      int key = ((ListItem*) Panel_get(super, i))->key;
      this->ss->fields[i] = key;
      if (key < LAST_PROCESSFIELD)
         this->ss->flags |= Process_fields[key].flags;
   }
   this->ss->fields[size] = 0;
}

global variable: ColumnsFunctions

static const char* const ColumnsFunctions[] = {"      ", "      ", "      ", "      ", "      ", "      ", "MoveUp", "MoveDn", "Remove", "Done  ", NULL};

ColumnsPanel.h

ColumnsPanel.h

function: ColumnsPanel_fill

void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss, Hashtable* columns);

function: ColumnsPanel_new

ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, Hashtable* columns, bool* changed);

function: ColumnsPanel_update

void ColumnsPanel_update(Panel* super);

struct: ColumnsPanel

typedef struct ColumnsPanel_ {
   Panel super;
   ScreenSettings* ss;
   bool* changed;

   bool moving;
} ColumnsPanel;

variable declaration: ColumnsPanel_class

extern const PanelClass ColumnsPanel_class;

CommandLine.c

CommandLine.c

function: CommandLine_delay

static void CommandLine_delay(Machine* host, unsigned long millisec) {
   struct timespec req = {
      .tv_sec = 0,
      .tv_nsec = millisec * 1000000L
   };
   while (nanosleep(&req, &req) == -1)
      continue;
   Platform_gettime_realtime(&host->realtime, &host->realtimeMs);
}

function: CommandLine_run

function: parseArguments

function: printHelpFlag

static void printHelpFlag(const char* name) {
   printf("%s " VERSION "\n"
          COPYRIGHT "\n"
          "Released under the GNU GPLv2+.\n\n"
          "-C --no-color                   Use a monochrome color scheme\n"
          "-d --delay=DELAY                Set the delay between updates, in tenths of seconds\n"
          "-F --filter=FILTER              Show only the commands matching the given filter\n"
          "-h --help                       Print this help screen\n"
          "-H --highlight-changes[=DELAY]  Highlight new and old processes\n", name);
#ifdef HAVE_GETMOUSE
   printf("-M --no-mouse                   Disable the mouse\n");
#endif
   printf("-n --max-iterations=NUMBER      Exit htop after NUMBER iterations/frame updates\n"
          "-p --pid=PID[,PID,PID...]       Show only the given PIDs\n"
          "   --readonly                   Disable all system and process changing features\n"
          "-s --sort-key=COLUMN            Sort by COLUMN in list view (try --sort-key=help for a list)\n"
          "-t --tree                       Show the tree view (can be combined with -s)\n"
          "-u --user[=USERNAME]            Show only processes for a given user (or $USER)\n"
          "-U --no-unicode                 Do not use unicode but plain ASCII\n"
          "-V --version                    Print version info\n");
   Platform_longOptionsUsage(name);
   printf("\n"
          "Press F1 inside %s for online help.\n"
          "See 'man %s' for more information.\n", name, name);
}

function: printVersionFlag

static void printVersionFlag(const char* name) {
   printf("%s " VERSION "\n", name);
}

function: setCommFilter

static void setCommFilter(State* state, char** commFilter) {
   Table* table = state->host->activeTable;
   IncSet* inc = state->mainPanel->inc;

   IncSet_setFilter(inc, *commFilter);
   table->incFilter = IncSet_filter(inc);

   free(*commFilter);
   *commFilter = NULL;
}

struct: CommandLineSettings_

typedef struct CommandLineSettings_ {
   Hashtable* pidMatchList;
   char* commFilter;
   uid_t userId;
   int sortKey;
   int delay;
   int iterationsRemaining;
   bool useColors;
#ifdef HAVE_GETMOUSE
   bool enableMouse;
#endif
   bool treeView;
   bool allowUnicode;
   bool highlightChanges;
   int highlightDelaySecs;
   bool readonly;
} CommandLineSettings;

CommandLine.h

CommandLine.h

enum: CommandLineStatus

typedef enum {
   STATUS_OK,
   STATUS_ERROR_EXIT,
   STATUS_OK_EXIT
} CommandLineStatus;

function: CommandLine_run

int CommandLine_run(int argc, char** argv);

variable: program

extern const char* program;

CommandScreen.c

CommandScreen.c

function: CommandScreen_delete

void CommandScreen_delete(Object* this) {
   free(InfoScreen_done((InfoScreen*)this));
}

function: CommandScreen_draw

static void CommandScreen_draw(InfoScreen* this) {
   InfoScreen_drawTitled(this, "Command of process %d - %s", Process_getPid(this->process), Process_getCommand(this->process));
}

function: CommandScreen_new

CommandScreen* CommandScreen_new(Process* process) {
   CommandScreen* this = AllocThis(CommandScreen);
   return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
}

function: CommandScreen_scan

static void CommandScreen_scan(InfoScreen* this) {
   Panel* panel = this->display;
   int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
   Panel_prune(panel);

   const char* p = Process_getCommand(this->process);
   char line[COLS + 1];
   int line_offset = 0, last_spc = -1, len;
   for (; *p != '\0'; p++, line_offset++) {
      assert(line_offset >= 0 && (size_t)line_offset < sizeof(line));
      line[line_offset] = *p;
      if (*p == ' ') {
         last_spc = line_offset;
      }

      if (line_offset == COLS) {
         len = (last_spc == -1) ? line_offset : last_spc;
         line[len] = '\0';
         InfoScreen_addLine(this, line);

         line_offset -= len;
         last_spc = -1;
         memcpy(line, p - line_offset, line_offset + 1);
      }
   }

   if (line_offset > 0) {
      line[line_offset] = '\0';
      InfoScreen_addLine(this, line);
   }

   Panel_setSelected(panel, idx);
}

struct: CommandScreen_class

const InfoScreenClass CommandScreen_class = {
   .super = {
      .extends = Class(Object),
      .delete = CommandScreen_delete
   },
   .scan = CommandScreen_scan,
   .draw = CommandScreen_draw
};

CommandScreen.h

CommandScreen.h

function: CommandScreen_delete

void CommandScreen_delete(Object* this);

function: CommandScreen_new

CommandScreen* CommandScreen_new(Process* process);

struct: CommandScreen_

typedef struct CommandScreen_ {
   InfoScreen super;
} CommandScreen;

typedef: CommandScreen

typedef struct CommandScreen_ {
   InfoScreen super;
} CommandScreen;

variable: CommandScreen_class

extern const InfoScreenClass CommandScreen_class;

Compat.c

Compat.c

function: Compat_faccessat

int Compat_faccessat(int dirfd,
                     const char* pathname,
                     int mode,
                     int flags) {
   int ret;

#ifdef HAVE_FACCESSAT

   // Implementation note: AT_SYMLINK_NOFOLLOW unsupported on FreeBSD, fallback to lstat in that case

   errno = 0;

   ret = faccessat(dirfd, pathname, mode, flags);
   if (!ret || errno != EINVAL)
      return ret;

#endif

   // Error out on unsupported configurations
   if (dirfd != (int)AT_FDCWD || mode != F_OK) {
      errno = EINVAL;
      return -1;
   }

   // Fallback to stat(2)/lstat(2) depending on flags
   struct stat sb;
   if (flags) {
      ret = lstat(pathname, &sb);
   } else {
      ret = stat(pathname, &sb);
   }

   return ret;
}

function: Compat_fstatat

int Compat_fstatat(int dirfd,
                   const char* dirpath,
                   const char* pathname,
                   struct stat* statbuf,
                   int flags) {

#ifdef HAVE_FSTATAT

   (void)dirpath;

   return fstatat(dirfd, pathname, statbuf, flags);

#else

   (void)dirfd;

   char path[4096];
   xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);

   if (flags & AT_SYMLINK_NOFOLLOW)
      return lstat(path, statbuf);

   return stat(path, statbuf);

#endif
}

function: Compat_openat

int Compat_openat(const char* dirpath,
                  const char* pathname,
                  int flags) {

   char path[4096];
   xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);

   return open(path, flags);
}

function: Compat_readlink

ssize_t Compat_readlink(openat_arg_t dirfd,
                        const char* pathname,
                        char* buf,
                        size_t bufsize) {

#ifdef HAVE_OPENAT

   char fdPath[32];
   xSnprintf(fdPath, sizeof(fdPath), "/proc/self/fd/%d", dirfd);

   char dirPath[PATH_MAX + 1];
   ssize_t r = readlink(fdPath, dirPath, sizeof(dirPath) - 1);
   if (r < 0)
      return r;

   dirPath[r] = '\0';

   char linkPath[PATH_MAX + 1];
   xSnprintf(linkPath, sizeof(linkPath), "%s/%s", dirPath, pathname);

#else

   char linkPath[PATH_MAX + 1];
   xSnprintf(linkPath, sizeof(linkPath), "%s/%s", dirfd, pathname);

#endif /* HAVE_OPENAT */

   return readlink(linkPath, buf, bufsize);
}

function: Compat_readlinkat

ssize_t Compat_readlinkat(int dirfd,
                          const char* dirpath,
                          const char* pathname,
                          char* buf,
                          size_t bufsize) {

#ifdef HAVE_READLINKAT

   (void)dirpath;

   return readlinkat(dirfd, pathname, buf, bufsize);

#else

   (void)dirfd;

   char path[4096];
   xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);

   return readlink(path, buf, bufsize);

#endif
}

Compat.h

Compat.h

function: Compat_faccessat

int Compat_faccessat(int dirfd,
                     const char* pathname,
                     int mode,
                     int flags);

function: Compat_fstatat

int Compat_fstatat(int dirfd,
                   const char* dirpath,
                   const char* pathname,
                   struct stat* statbuf,
                   int flags);

function: Compat_openat (!HAVE_OPENAT)

int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags);

function: Compat_openat (HAVE_OPENAT)

static inline int Compat_openat(openat_arg_t dirfd, const char* pathname, int flags) {
   return openat(dirfd, pathname, flags);
}

function: Compat_openatArgClose (!HAVE_OPENAT)

static inline void Compat_openatArgClose(openat_arg_t dirpath) {
   (void)dirpath;
}

function: Compat_openatArgClose (HAVE_OPENAT)

static inline void Compat_openatArgClose(openat_arg_t dirfd) {
   close(dirfd);
}

function: Compat_readlink

ssize_t Compat_readlink(openat_arg_t dirfd,
                        const char* pathname,
                        char* buf,
                        size_t bufsize);

function: Compat_readlinkat

ssize_t Compat_readlinkat(int dirfd,
                          const char* dirpath,
                          const char* pathname,
                          char* buf,
                          size_t bufsize);

type: openat_arg_t (!HAVE_OPENAT)

typedef const char* openat_arg_t;

type: openat_arg_t (HAVE_OPENAT)

typedef int openat_arg_t;

configure.ac

configure.ac

file: configure.ac

CONTRIBUTING.md

CONTRIBUTING.md

file: CONTRIBUTING.md

Contributing Guide
==================

Thank you so much for taking the time to contribute in to htop!

Bug Reports
-----------

Bug reports should be posted in the [Github issue
tracker](https://github.com/htop-dev/htop/issues).
Bug reports are extremely important since it's impossible for us to test
htop in every possible system, distribution and scenario. Your feedback
is what keeps the tool stable and always improving!  Thank you!

Pull Requests
-------------

Code contributions are most welcome! Just [fork the
repo](https://github.com/htop-dev/htop) and send a [pull
request](https://github.com/htop-dev/htop/pulls).  Help is especially
appreciated for support of platforms other than Linux.  If proposing new
features, please be mindful that htop is a system tool that needs to keep a
small footprint and perform well on systems under stress -- so unfortunately
we can't accept every new feature proposed, as we need to keep the tool slim
and maintainable.  Great ideas backed by a PR are always carefully considered
for inclusion though!  Also, PRs containing bug fixes and portability tweaks
are always included, please send those in!

Feature Requests
----------------

Please label Github issues that are feature requests with one of the `feature request`
labels. If you can't do this yourself, don't worry. The friendly folks from the
core team will distribute and fixup Github labels as part of the regular reviews.

Style Guide
-----------

To make working with the code easier a set of guidelines have evolved in
the past that new contributions should try to follow. While they are not set
in stone and always up for changes should the need arise they still provide
a first orientation to go by when contributing to this repository.

The details of the coding style as well as what to take care about with your
contributions can be found in our [style guide](docs/styleguide.md).

COPYING

COPYING

file: COPYING

CPUMeter.c

CPUMeter.c

function: AllCPUsMeter_done

static void AllCPUsMeter_done(Meter* this) {
   CPUMeterData* data = this->meterData;
   Meter** meters = data->meters;
   int start, count;
   AllCPUsMeter_getRange(this, &start, &count);
   for (int i = 0; i < count; i++)
      Meter_delete((Object*)meters[i]);
   free(data->meters);
   free(data);
}

function: AllCPUsMeter_getRange

static void AllCPUsMeter_getRange(const Meter* this, int* start, int* count) {
   unsigned int cpus = this->host->existingCPUs;
   switch (Meter_name(this)[0]) {
      default:
      case 'A': // All
         *start = 0;
         *count = cpus;
         break;
      case 'L': // First Half
         *start = 0;
         *count = (cpus + 1) / 2;
         break;
      case 'R': // Second Half
         *start = (cpus + 1) / 2;
         *count = cpus - *start;
         break;
   }
}

function: AllCPUsMeter_updateValues

static void AllCPUsMeter_updateValues(Meter* this) {
   CPUMeterData* data = this->meterData;
   Meter** meters = data->meters;
   int start, count;
   AllCPUsMeter_getRange(this, &start, &count);
   for (int i = 0; i < count; i++)
      Meter_updateValues(meters[i]);
}

function: CPUMeter_display

function: CPUMeter_getUiName

static void CPUMeter_getUiName(const Meter* this, char* buffer, size_t length) {
   assert(length > 0);

   if (this->param > 0)
      xSnprintf(buffer, length, "%s %u", Meter_uiName(this), this->param);
   else
      xSnprintf(buffer, length, "%s", Meter_uiName(this));
}

function: CPUMeter_init

static void CPUMeter_init(Meter* this) {
   unsigned int cpu = this->param;
   const Machine* host = this->host;
   if (cpu == 0) {
      Meter_setCaption(this, "Avg");
   } else if (host->activeCPUs > 1) {
      char caption[10];
      xSnprintf(caption, sizeof(caption), "%3u", Settings_cpuId(host->settings, cpu - 1));
      Meter_setCaption(this, caption);
   }
}

function: CPUMeter_updateValues

function: CPUMeterCommonDraw

static void CPUMeterCommonDraw(Meter* this, int x, int y, int w, int ncol) {
   CPUMeterData* data = this->meterData;
   Meter** meters = data->meters;
   int start, count;
   AllCPUsMeter_getRange(this, &start, &count);
   int colwidth = (w - ncol) / ncol + 1;
   int diff = (w - (colwidth * ncol));
   int nrows = (count + ncol - 1) / ncol;
   for (int i = 0; i < count; i++) {
      int d = (i / nrows) > diff ? diff : (i / nrows); // dynamic spacer
      int xpos = x + ((i / nrows) * colwidth) + d;
      int ypos = y + ((i % nrows) * meters[0]->h);
      meters[i]->draw(meters[i], xpos, ypos, colwidth);
   }
}

function: CPUMeterCommonInit

static void CPUMeterCommonInit(Meter* this) {
   int start, count;
   AllCPUsMeter_getRange(this, &start, &count);

   CPUMeterData* data = this->meterData;
   if (!data) {
      data = this->meterData = xMalloc(sizeof(CPUMeterData));
      data->cpus = this->host->existingCPUs;
      data->meters = count ? xCalloc(count, sizeof(Meter*)) : NULL;
   }

   Meter** meters = data->meters;
   for (int i = 0; i < count; i++) {
      if (!meters[i])
         meters[i] = Meter_new(this->host, start + i + 1, (const MeterClass*) Class(CPUMeter));

      Meter_init(meters[i]);
   }
}

function: CPUMeterCommonUpdateMode

static void CPUMeterCommonUpdateMode(Meter* this, MeterModeId mode, int ncol) {
   CPUMeterData* data = this->meterData;
   Meter** meters = data->meters;
   this->mode = mode;
   int start, count;
   AllCPUsMeter_getRange(this, &start, &count);
   if (!count) {
      this->h = 1;
      return;
   }
   for (int i = 0; i < count; i++) {
      Meter_setMode(meters[i], mode);
   }
   int h = meters[0]->h;
   assert(h > 0);
   this->h = h * ((count + ncol - 1) / ncol);
}

function: DualColCPUsMeter_draw

static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
   CPUMeterCommonDraw(this, x, y, w, 2);
}

function: DualColCPUsMeter_updateMode

static void DualColCPUsMeter_updateMode(Meter* this, MeterModeId mode) {
   CPUMeterCommonUpdateMode(this, mode, 2);
}

function: OctoColCPUsMeter_draw

static void OctoColCPUsMeter_draw(Meter* this, int x, int y, int w) {
   CPUMeterCommonDraw(this, x, y, w, 8);
}

function: OctoColCPUsMeter_updateMode

static void OctoColCPUsMeter_updateMode(Meter* this, MeterModeId mode) {
   CPUMeterCommonUpdateMode(this, mode, 8);
}

function: QuadColCPUsMeter_draw

static void QuadColCPUsMeter_draw(Meter* this, int x, int y, int w) {
   CPUMeterCommonDraw(this, x, y, w, 4);
}

function: QuadColCPUsMeter_updateMode

static void QuadColCPUsMeter_updateMode(Meter* this, MeterModeId mode) {
   CPUMeterCommonUpdateMode(this, mode, 4);
}

function: SingleColCPUsMeter_draw

static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
   CPUMeterData* data = this->meterData;
   Meter** meters = data->meters;
   int start, count;
   AllCPUsMeter_getRange(this, &start, &count);
   for (int i = 0; i < count; i++) {
      meters[i]->draw(meters[i], x, y, w);
      y += meters[i]->h;
   }
}

function: SingleColCPUsMeter_updateMode

static void SingleColCPUsMeter_updateMode(Meter* this, MeterModeId mode) {
   CPUMeterCommonUpdateMode(this, mode, 1);
}

global variable: AllCPUs2Meter_class

const MeterClass AllCPUs2Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "AllCPUs2",
   .uiName = "CPUs (1&2/2)",
   .description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
   .caption = "CPU",
   .draw = DualColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = DualColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: AllCPUs4Meter_class

const MeterClass AllCPUs4Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "AllCPUs4",
   .uiName = "CPUs (1&2&3&4/4)",
   .description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
   .caption = "CPU",
   .draw = QuadColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = QuadColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: AllCPUs8Meter_class

const MeterClass AllCPUs8Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "AllCPUs8",
   .uiName = "CPUs (1-8/8)",
   .description = "CPUs (1-8/8): all CPUs in 8 shorter columns",
   .caption = "CPU",
   .draw = OctoColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = OctoColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: AllCPUsMeter_class

const MeterClass AllCPUsMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "AllCPUs",
   .uiName = "CPUs (1/1)",
   .description = "CPUs (1/1): all CPUs",
   .caption = "CPU",
   .draw = SingleColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = SingleColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: CPUMeter_attributes

static const int CPUMeter_attributes[] = {
   CPU_NICE,
   CPU_NORMAL,
   CPU_SYSTEM,
   CPU_IRQ,
   CPU_SOFTIRQ,
   CPU_STEAL,
   CPU_GUEST,
   CPU_IOWAIT
};

global variable: CPUMeter_class

const MeterClass CPUMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = CPUMeter_updateValues,
   .getUiName = CPUMeter_getUiName,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = CPU_METER_ITEMCOUNT,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "CPU",
   .uiName = "CPU",
   .caption = "CPU",
   .init = CPUMeter_init
};

global variable: LeftCPUs2Meter_class

const MeterClass LeftCPUs2Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "LeftCPUs2",
   .uiName = "CPUs (1&2/4)",
   .description = "CPUs (1&2/4): first half in 2 shorter columns",
   .caption = "CPU",
   .draw = DualColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = DualColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: LeftCPUs4Meter_class

const MeterClass LeftCPUs4Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "LeftCPUs4",
   .uiName = "CPUs (1-4/8)",
   .description = "CPUs (1-4/8): first half in 4 shorter columns",
   .caption = "CPU",
   .draw = QuadColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = QuadColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: LeftCPUs8Meter_class

const MeterClass LeftCPUs8Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "LeftCPUs8",
   .uiName = "CPUs (1-8/16)",
   .description = "CPUs (1-8/16): first half in 8 shorter columns",
   .caption = "CPU",
   .draw = OctoColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = OctoColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: LeftCPUsMeter_class

const MeterClass LeftCPUsMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "LeftCPUs",
   .uiName = "CPUs (1/2)",
   .description = "CPUs (1/2): first half of list",
   .caption = "CPU",
   .draw = SingleColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = SingleColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: RightCPUs2Meter_class

const MeterClass RightCPUs2Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "RightCPUs2",
   .uiName = "CPUs (3&4/4)",
   .description = "CPUs (3&4/4): second half in 2 shorter columns",
   .caption = "CPU",
   .draw = DualColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = DualColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: RightCPUs4Meter_class

const MeterClass RightCPUs4Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "RightCPUs4",
   .uiName = "CPUs (5-8/8)",
   .description = "CPUs (5-8/8): second half in 4 shorter columns",
   .caption = "CPU",
   .draw = QuadColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = QuadColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: RightCPUs8Meter_class

const MeterClass RightCPUs8Meter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "RightCPUs8",
   .uiName = "CPUs (9-16/16)",
   .description = "CPUs (9-16/16): second half in 8 shorter columns",
   .caption = "CPU",
   .draw = OctoColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = OctoColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

global variable: RightCPUsMeter_class

const MeterClass RightCPUsMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = CPUMeter_display
   },
   .updateValues = AllCPUsMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .total = 100.0,
   .attributes = CPUMeter_attributes,
   .name = "RightCPUs",
   .uiName = "CPUs (2/2)",
   .description = "CPUs (2/2): second half of list",
   .caption = "CPU",
   .draw = SingleColCPUsMeter_draw,
   .init = CPUMeterCommonInit,
   .updateMode = SingleColCPUsMeter_updateMode,
   .done = AllCPUsMeter_done
};

struct: CPUMeterData_

typedef struct CPUMeterData_ {
   unsigned int cpus;
   Meter** meters;
} CPUMeterData;

CPUMeter.h

CPUMeter.h

enum: CPUMeterValues

typedef enum {
   CPU_METER_NICE = 0,
   CPU_METER_NORMAL = 1,
   CPU_METER_KERNEL = 2,
   CPU_METER_IRQ = 3,
   CPU_METER_SOFTIRQ = 4,
   CPU_METER_STEAL = 5,
   CPU_METER_GUEST = 6,
   CPU_METER_IOWAIT = 7,
   CPU_METER_FREQUENCY = 8,
   CPU_METER_TEMPERATURE = 9,
   CPU_METER_ITEMCOUNT = 10 // number of entries in this enum
} CPUMeterValues;

variable: AllCPUs2Meter_class

extern const MeterClass AllCPUs2Meter_class;

variable: AllCPUs4Meter_class

extern const MeterClass AllCPUs4Meter_class;

variable: AllCPUs8Meter_class

extern const MeterClass AllCPUs8Meter_class;

variable: AllCPUsMeter_class

extern const MeterClass AllCPUsMeter_class;

variable: CPUMeter_class

extern const MeterClass CPUMeter_class;

variable: LeftCPUs2Meter_class

extern const MeterClass LeftCPUs2Meter_class;

variable: LeftCPUs4Meter_class

extern const MeterClass LeftCPUs4Meter_class;

variable: LeftCPUs8Meter_class

extern const MeterClass LeftCPUs8Meter_class;

variable: LeftCPUsMeter_class

extern const MeterClass LeftCPUsMeter_class;

variable: RightCPUs2Meter_class

extern const MeterClass RightCPUs2Meter_class;

variable: RightCPUs4Meter_class

extern const MeterClass RightCPUs4Meter_class;

variable: RightCPUs8Meter_class

extern const MeterClass RightCPUs8Meter_class;

variable: RightCPUsMeter_class

extern const MeterClass RightCPUsMeter_class;

CRT.c

CRT.c

file: CRT.c

CRT.h

CRT.h

enum: ColorElements

enum: ColorScheme

typedef enum ColorScheme_ {
   COLORSCHEME_DEFAULT,
   COLORSCHEME_MONOCHROME,
   COLORSCHEME_BLACKONWHITE,
   COLORSCHEME_LIGHTTERMINAL,
   COLORSCHEME_MIDNIGHT,
   COLORSCHEME_BLACKNIGHT,
   COLORSCHEME_BROKENGRAY,
   LAST_COLORSCHEME
} ColorScheme;

enum: TreeStr

typedef enum TreeStr_ {
   TREE_STR_VERT,
   TREE_STR_RTEE,
   TREE_STR_BEND,
   TREE_STR_TEND,
   TREE_STR_OPEN,
   TREE_STR_SHUT,
   TREE_STR_ASC,
   TREE_STR_DESC,
   LAST_TREE_STR
} TreeStr;

function: CRT_debug_impl

void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) ATTR_FORMAT(printf, 4, 5);

function: CRT_disableDelay

void CRT_disableDelay(void);

function: CRT_done

void CRT_done(void);

function: CRT_enableDelay

void CRT_enableDelay(void);

function: CRT_fatalError

void CRT_fatalError(const char* note) ATTR_NORETURN;

function: CRT_handleSIGSEGV

void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;

function: CRT_init

void CRT_init(const Settings* settings, bool allowUnicode, bool retainScreenOnExit);

function: CRT_readKey

int CRT_readKey(void);

function: CRT_resetSignalHandlers

void CRT_resetSignalHandlers(void);

function: CRT_setColors

void CRT_setColors(int colorScheme);

function: CRT_setMouse

#ifdef HAVE_GETMOUSE
void CRT_setMouse(bool enabled);
#else
#define CRT_setMouse(enabled)
#endif

function: CRT_updateDelay

static inline void CRT_updateDelay(void) {
   CRT_enableDelay(); // pushes new delay setting into halfdelay(3X)
}

global_variable: CRT_colors

extern const int* CRT_colors;

global_variable: CRT_colorScheme

extern ColorScheme CRT_colorScheme;

global_variable: CRT_cursorX

extern int CRT_cursorX;

global_variable: CRT_degreeSign

extern const char* CRT_degreeSign;

global_variable: CRT_scrollHAmount

extern int CRT_scrollHAmount;

global_variable: CRT_scrollWheelVAmount

extern int CRT_scrollWheelVAmount;

global_variable: CRT_treeStr

extern const char* const* CRT_treeStr;

global_variable: CRT_utf8

extern bool CRT_utf8;

macro: CRT_debug

#ifdef NDEBUG
# define CRT_debug(...)
#else
void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) ATTR_FORMAT(printf, 4, 5);
# define CRT_debug(...) CRT_debug_impl(__FILE__, __LINE__, __func__, __VA_ARGS__)
#endif

macro: KEY_ALT

#define KEY_ALT(x)    (KEY_F(64 - 26) + ((x) - 'A'))

macro: KEY_DEL_MAC

#define KEY_DEL_MAC   127

macro: KEY_FOCUS_IN

#define KEY_FOCUS_IN  (KEY_MAX + 'I')

macro: KEY_FOCUS_OUT

#define KEY_FOCUS_OUT (KEY_MAX + 'O')

macro: KEY_RECLICK

#define KEY_RECLICK   KEY_F(32)

macro: KEY_SHIFT_TAB

#define KEY_SHIFT_TAB KEY_F(33)

macro: KEY_WHEELDOWN

#define KEY_WHEELDOWN KEY_F(31)

macro: KEY_WHEELUP

#define KEY_WHEELUP   KEY_F(30)

macro: SCREEN_TAB_COLUMN_GAP

#define SCREEN_TAB_COLUMN_GAP  1

macro: SCREEN_TAB_MARGIN_LEFT

#define SCREEN_TAB_MARGIN_LEFT 2

DarwinMachine.c

darwin/DarwinMachine.c

function: DarwinMachine_allocateCPULoadInfo

static unsigned DarwinMachine_allocateCPULoadInfo(processor_cpu_load_info_t* p) {
   mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t);
   unsigned cpu_count;

   // TODO Improving the accuracy of the load counts would help a lot.
   if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) {
      CRT_fatalError("Unable to retrieve CPU info");
   }

   return cpu_count;
}

function: DarwinMachine_freeCPULoadInfo

static void DarwinMachine_freeCPULoadInfo(processor_cpu_load_info_t* p) {
   if (!p)
      return;

   if (!*p)
      return;

   if (0 != munmap(*p, vm_page_size)) {
      CRT_fatalError("Unable to free old CPU load information");
   }

   *p = NULL;
}

function: DarwinMachine_getHostInfo

static void DarwinMachine_getHostInfo(host_basic_info_data_t* p) {
   mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;

   if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
      CRT_fatalError("Unable to retrieve host info");
   }
}

function: DarwinMachine_getVMStats

static void DarwinMachine_getVMStats(DarwinMachine* this) {
#ifdef HAVE_STRUCT_VM_STATISTICS64
   mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT;

   if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info_t)&this->vm_stats, &info_size) != 0) {
      CRT_fatalError("Unable to retrieve VM statistics64");
   }
#else
   mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;

   if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&this->vm_stats, &info_size) != 0) {
      CRT_fatalError("Unable to retrieve VM statistics");
   }
#endif
}

function: Machine_delete

void Machine_delete(Machine* super) {
   DarwinMachine* this = (DarwinMachine*) super;

   DarwinMachine_freeCPULoadInfo(&this->prev_load);

   Machine_done(super);
   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* host, unsigned int id) {
   assert(id < host->existingCPUs);

   // TODO: support offline CPUs and hot swapping
   (void) host; (void) id;

   return true;
}

function: Machine_new

Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
   DarwinMachine* this = xCalloc(1, sizeof(DarwinMachine));
   Machine* super = &this->super;

   Machine_init(super, usersTable, userId);

   /* Initialize the CPU information */
   super->activeCPUs = DarwinMachine_allocateCPULoadInfo(&this->prev_load);
   super->existingCPUs = super->activeCPUs;
   DarwinMachine_getHostInfo(&this->host_info);
   DarwinMachine_allocateCPULoadInfo(&this->curr_load);

   /* Initialize the VM statistics */
   DarwinMachine_getVMStats(this);

   /* Initialize the ZFS kstats, if zfs.kext loaded */
   openzfs_sysctl_init(&this->zfs);
   openzfs_sysctl_updateArcStats(&this->zfs);

   return super;
}

function: Machine_scan

void Machine_scan(Machine* super) {
   DarwinMachine* host = (DarwinMachine*) super;

   /* Update the global data (CPU times and VM stats) */
   DarwinMachine_freeCPULoadInfo(&host->prev_load);
   host->prev_load = host->curr_load;
   DarwinMachine_allocateCPULoadInfo(&host->curr_load);
   DarwinMachine_getVMStats(host);
   openzfs_sysctl_updateArcStats(&host->zfs);
}

DarwinMachine.h

darwin/DarwinMachine.h

struct: DarwinMachine

typedef struct DarwinMachine_ {
   Machine super;

   host_basic_info_data_t host_info;
#ifdef HAVE_STRUCT_VM_STATISTICS64
   vm_statistics64_data_t vm_stats;
#else
   vm_statistics_data_t vm_stats;
#endif
   processor_cpu_load_info_t prev_load;
   processor_cpu_load_info_t curr_load;

   ZfsArcStats zfs;
} DarwinMachine;

DarwinProcess.c

darwin/DarwinProcess.c

function: DarwinProcess_compareByKey

static int DarwinProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const DarwinProcess* p1 = (const DarwinProcess*)v1;
   const DarwinProcess* p2 = (const DarwinProcess*)v2;

   switch (key) {
   // add Platform-specific fields here
   case TRANSLATED:
      return SPACESHIP_NUMBER(p1->translated, p2->translated);
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

function: DarwinProcess_getDevname

static char* DarwinProcess_getDevname(dev_t dev) {
   if (dev == NODEV) {
      return NULL;
   }
   char buf[sizeof("/dev/") + MAXNAMLEN];
   char* name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN);
   if (name) {
      return xStrdup(name);
   }
   return NULL;
}

function: DarwinProcess_new

Process* DarwinProcess_new(const Machine* host) {
   DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess));
   Object_setClass(this, Class(DarwinProcess));
   Process_init(&this->super, host);

   this->utime = 0;
   this->stime = 0;
   this->taskAccess = true;
   this->translated = false;
   this->super.state = UNKNOWN;

   return (Process*)this;
}

function: DarwinProcess_rowWriteField

static void DarwinProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const DarwinProcess* dp = (const DarwinProcess*) super;

   char buffer[256]; buffer[255] = '\0';
   int attr = CRT_colors[DEFAULT_COLOR];
   size_t n = sizeof(buffer) - 1;

   switch (field) {
   // add Platform-specific fields here
   case TRANSLATED: xSnprintf(buffer, n, "%c ", dp->translated ? 'T' : 'N'); break;
   default:
      Process_writeField(&dp->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

function: DarwinProcess_scanThreads

function: DarwinProcess_setFromKInfoProc

function: DarwinProcess_setFromLibprocPidinfo

void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable* dpt, double timeIntervalNS) {
   struct proc_taskinfo pti;

   if (PROC_PIDTASKINFO_SIZE != proc_pidinfo(Process_getPid(&proc->super), PROC_PIDTASKINFO, 0, &pti, PROC_PIDTASKINFO_SIZE)) {
      proc->taskAccess = false;
      return;
   }

   const DarwinMachine* dhost = (const DarwinMachine*) proc->super.super.host;

   uint64_t total_existing_time_ns = proc->stime + proc->utime;
   uint64_t user_time_ns = pti.pti_total_user;
   uint64_t system_time_ns = pti.pti_total_system;
   uint64_t total_current_time_ns = user_time_ns + system_time_ns;

   if (total_existing_time_ns < total_current_time_ns) {
      const uint64_t total_time_diff_ns = total_current_time_ns - total_existing_time_ns;
      proc->super.percent_cpu = ((double)total_time_diff_ns / timeIntervalNS) * 100.0;
   } else {
      proc->super.percent_cpu = 0.0;
   }
   Process_updateCPUFieldWidths(proc->super.percent_cpu);

   proc->super.state = pti.pti_numrunning > 0 ? RUNNING : SLEEPING;
   // Convert from nanoseconds to hundredths of seconds
   proc->super.time = total_current_time_ns / 10000000ULL;
   proc->super.nlwp = pti.pti_threadnum;
   proc->super.m_virt = pti.pti_virtual_size / ONE_K;
   proc->super.m_resident = pti.pti_resident_size / ONE_K;
   proc->super.majflt = pti.pti_faults;
   proc->super.percent_mem = (double)pti.pti_resident_size * 100.0 / (double)dhost->host_info.max_mem;

   proc->stime = system_time_ns;
   proc->utime = user_time_ns;

   dpt->super.kernelThreads += 0; /*pti.pti_threads_system;*/
   dpt->super.userlandThreads += pti.pti_threadnum; /*pti.pti_threads_user;*/
   dpt->super.totalTasks += pti.pti_threadnum;
   dpt->super.runningTasks += pti.pti_numrunning;
}

function: DarwinProcess_updateCmdLine

function: DarwinProcess_updateCwd

static void DarwinProcess_updateCwd(pid_t pid, Process* proc) {
   struct proc_vnodepathinfo vpi;

   int r = proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi));
   if (r <= 0) {
      free(proc->procCwd);
      proc->procCwd = NULL;
      return;
   }

   if (!vpi.pvi_cdir.vip_path[0]) {
      free(proc->procCwd);
      proc->procCwd = NULL;
      return;
   }

   free_and_xStrdup(&proc->procCwd, vpi.pvi_cdir.vip_path);
}

function: DarwinProcess_updateExe

static void DarwinProcess_updateExe(pid_t pid, Process* proc) {
   char path[PROC_PIDPATHINFO_MAXSIZE];

   int r = proc_pidpath(pid, path, sizeof(path));
   if (r <= 0)
      return;

   Process_updateExe(proc, path);
}

function: Process_delete

void Process_delete(Object* cast) {
   DarwinProcess* this = (DarwinProcess*) cast;
   Process_done(&this->super);
   // free platform-specific fields here
   free(this);
}

global_constant: DarwinProcess_class

const ProcessClass DarwinProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = DarwinProcess_rowWriteField
   },
   .compareByKey = DarwinProcess_compareByKey
};

global_constant: Process_fields

DarwinProcess.h

darwin/DarwinProcess.h

extern const: DarwinProcess_class

extern const ProcessClass DarwinProcess_class;

extern const: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

function: DarwinProcess_new

Process* DarwinProcess_new(const Machine* settings);

function: DarwinProcess_scanThreads

void DarwinProcess_scanThreads(DarwinProcess* dp, DarwinProcessTable* dpt);

function: DarwinProcess_setFromKInfoProc

void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists);

function: DarwinProcess_setFromLibprocPidinfo

void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessTable* dpt, double timeIntervalNS);

function: Process_delete

void Process_delete(Object* cast);

macro: PROCESS_FLAG_TTY

#define PROCESS_FLAG_TTY 0x00000100

struct: DarwinProcess_

typedef struct DarwinProcess_ {
   Process super;

   uint64_t utime;
   uint64_t stime;
   bool taskAccess;
   bool translated;
} DarwinProcess;

DarwinProcessTable.c

darwin/DarwinProcessTable.c

function: ProcessTable_delete

void ProcessTable_delete(Object* cast) {
   DarwinProcessTable* this = (DarwinProcessTable*) cast;
   ProcessTable_done(&this->super);
   free(this);
}

function: ProcessTable_getKInfoProcs

static struct kinfo_proc* ProcessTable_getKInfoProcs(size_t* count) {
   int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
   struct kinfo_proc* processes = NULL;

   for (unsigned int retry = 0; retry < 4; retry++) {
      size_t size = 0;
      if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) {
         CRT_fatalError("Unable to get size of kproc_infos");
      }

      size += 16 * retry * retry * sizeof(struct kinfo_proc);
      processes = xRealloc(processes, size);

      if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) {
         *count = size / sizeof(struct kinfo_proc);
         return processes;
      }

      if (errno != ENOMEM)
         break;
   }

   CRT_fatalError("Unable to get kinfo_procs");
}

function: ProcessTable_goThroughEntries

function: ProcessTable_new

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
   DarwinProcessTable* this = xCalloc(1, sizeof(DarwinProcessTable));
   Object_setClass(this, Class(ProcessTable));

   ProcessTable* super = &this->super;
   ProcessTable_init(super, Class(DarwinProcess), host, pidMatchList);

   return super;
}

DarwinProcessTable.h

darwin/DarwinProcessTable.h

struct: DarwinProcessTable_

typedef struct DarwinProcessTable_ {
   ProcessTable super;

   uint64_t global_diff;
} DarwinProcessTable;

Platform.c

darwin/Platform.c

file: Platform.c

Platform.h

darwin/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

void Platform_gettime_monotonic(uint64_t* msec);

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_schedulerTicksToNanoseconds

double Platform_schedulerTicksToNanoseconds(const double scheduler_ticks);

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* mtr, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* mtr);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* mtr);

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this);

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this);

macro: PLATFORM_LONG_OPTIONS

#define PLATFORM_LONG_OPTIONS

variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

variable: Platform_signals

extern const SignalItem Platform_signals[];

PlatformHelpers.c

darwin/PlatformHelpers.c

function: Platform_CompareKernelVersion

int Platform_CompareKernelVersion(KernelVersion v) {
   struct KernelVersion actualVersion;
   Platform_GetKernelVersion(&actualVersion);

   if (actualVersion.major != v.major) {
      return actualVersion.major - v.major;
   }
   if (actualVersion.minor != v.minor) {
      return actualVersion.minor - v.minor;
   }
   if (actualVersion.patch != v.patch) {
      return actualVersion.patch - v.patch;
   }

   return 0;
}

function: Platform_getCPUBrandString

void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize) {
   if (sysctlbyname("machdep.cpu.brand_string", cpuBrandString, &cpuBrandStringSize, NULL, 0) == -1) {
      fprintf(stderr,
         "WARN: Unable to determine the CPU brand string.\n"
         "errno: %i, %s\n", errno, strerror(errno));

      String_safeStrncpy(cpuBrandString, "UNKNOWN!", cpuBrandStringSize);
   }
}

function: Platform_GetKernelVersion

void Platform_GetKernelVersion(KernelVersion* k) {
   static KernelVersion cachedKernelVersion;

   if (!cachedKernelVersion.major) {
      // just in case it fails someday
      cachedKernelVersion = (KernelVersion) { -1, -1, -1 };
      char str[256] = {0};
      size_t size = sizeof(str);
      int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
      if (ret == 0) {
         sscanf(str, "%hd.%hd.%hd", &cachedKernelVersion.major, &cachedKernelVersion.minor, &cachedKernelVersion.patch);
      }
   }
   memcpy(k, &cachedKernelVersion, sizeof(cachedKernelVersion));
}

function: Platform_isRunningTranslated

bool Platform_isRunningTranslated(void) {
   int ret = 0;
   size_t size = sizeof(ret);
   errno = 0;
   if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
      if (errno == ENOENT)
         return false;

      fprintf(stderr,
         "WARN: Could not determine if this process was running in a translation environment like Rosetta 2.\n"
         "Assuming that we're not.\n"
         "errno: %i, %s\n", errno, strerror(errno));

      return false;
   }
   return ret;
}

function: Platform_KernelVersionIsBetween

bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upperBound) {
   return 0 <= Platform_CompareKernelVersion(lowerBound)
      && Platform_CompareKernelVersion(upperBound) < 0;
}

PlatformHelpers.h

darwin/PlatformHelpers.h

function: Platform_CompareKernelVersion

int Platform_CompareKernelVersion(KernelVersion v);

function: Platform_getCPUBrandString

void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize);

function: Platform_GetKernelVersion

void Platform_GetKernelVersion(KernelVersion* k);

function: Platform_isRunningTranslated

bool Platform_isRunningTranslated(void);

function: Platform_KernelVersionIsBetween

bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upperBound);

struct: KernelVersion

typedef struct KernelVersion {
   short int major;
   short int minor;
   short int patch;
} KernelVersion;

ProcessField.h

darwin/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   TRANSLATED = 100,             \
                                 \
   DUMMY_BUMP_FIELD = CWD,       \
   // End of list

DateMeter.c

DateMeter.c

function: DateMeter_updateValues

static void DateMeter_updateValues(Meter* this) {
   const Machine* host = this->host;

   struct tm result;
   const struct tm* lt = localtime_r(&host->realtime.tv_sec, &result);
   strftime(this->txtBuffer, sizeof(this->txtBuffer), "%F", lt);
}

global variable: DateMeter_attributes

static const int DateMeter_attributes[] = {
   DATE
};

global variable (struct instance): DateMeter_class

const MeterClass DateMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = DateMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE) | (1 << LED_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = DateMeter_attributes,
   .name = "Date",
   .uiName = "Date",
   .caption = "Date: ",
};

DateMeter.h

DateMeter.h

variable: DateMeter_class

extern const MeterClass DateMeter_class;

DateTimeMeter.c

DateTimeMeter.c

function: DateTimeMeter_updateValues

static void DateTimeMeter_updateValues(Meter* this) {
   const Machine* host = this->host;

   struct tm result;
   const struct tm* lt = localtime_r(&host->realtime.tv_sec, &result);
   strftime(this->txtBuffer, sizeof(this->txtBuffer), "%F %H:%M:%S", lt);
}

struct: DateTimeMeter_class

const MeterClass DateTimeMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = DateTimeMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE) | (1 << LED_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = DateTimeMeter_attributes,
   .name = "DateTime",
   .uiName = "Date and Time",
   .caption = "Date & Time: ",
};

variable: DateTimeMeter_attributes

static const int DateTimeMeter_attributes[] = {
   DATETIME
};

DateTimeMeter.h

DateTimeMeter.h

struct: DateTimeMeter_class

extern const MeterClass DateTimeMeter_class;

DiskIOMeter.c

DiskIOMeter.c

function: DiskIOMeter_display

static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
   switch (status) {
      case RATESTATUS_NODATA:
         RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
         return;
      case RATESTATUS_INIT:
         RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
         return;
      case RATESTATUS_STALE:
         RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
         return;
      case RATESTATUS_DATA:
         break;
   }

   char buffer[16];

   int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
   int len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
   RichString_appendnAscii(out, CRT_colors[color], buffer, len);

   RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_read_diff_str);
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");

   RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str);
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
}

function: DiskIOMeter_updateValues

global variable: cached_read_diff_str

static char cached_read_diff_str[6];

global variable: cached_utilisation_diff

static double cached_utilisation_diff;

global variable: cached_utilisation_norm

static double cached_utilisation_norm;

global variable: cached_write_diff_str

static char cached_write_diff_str[6];

global variable: DiskIOMeter_attributes

static const int DiskIOMeter_attributes[] = {
   METER_VALUE_NOTICE,
   METER_VALUE_IOREAD,
   METER_VALUE_IOWRITE,
};

global variable: status

static MeterRateStatus status = RATESTATUS_INIT;

struct: DiskIOMeter_class

const MeterClass DiskIOMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = DiskIOMeter_display
   },
   .updateValues = DiskIOMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 1,
   .total = 1.0,
   .attributes = DiskIOMeter_attributes,
   .name = "DiskIO",
   .uiName = "Disk IO",
   .caption = "Disk IO: "
};

DiskIOMeter.h

DiskIOMeter.h

extern declaration: DiskIOMeter_class

extern const MeterClass DiskIOMeter_class;

struct: DiskIOData

typedef struct DiskIOData_ {
   uint64_t totalBytesRead;
   uint64_t totalBytesWritten;
   uint64_t totalMsTimeSpend;
   uint64_t numDisks;
} DiskIOData;

DisplayOptionsPanel.c

DisplayOptionsPanel.c

constant array: DisplayOptionsDecIncEvents

static const int DisplayOptionsDecIncEvents[] = {'-', KEY_F(7), '+', KEY_F(8), ERR, KEY_F(10)};

constant array: DisplayOptionsDecIncFunctions

static const char* const DisplayOptionsDecIncFunctions[] = {"      ", "      ", "      ", "      ", "      ", "      ", "Dec   ", "Inc   ", "      ", "Done  ", NULL};

constant array: DisplayOptionsDecIncKeys

static const char* const DisplayOptionsDecIncKeys[] =      {"  "    , "  "    , "  "    , "  "    , "  "    , "  "    , "F7"    , "F8"    , "  "    , "F10"   , NULL};

constant array: DisplayOptionsFunctions

static const char* const DisplayOptionsFunctions[] =       {"      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "Done  ", NULL};

function: DisplayOptionsPanel_delete

static void DisplayOptionsPanel_delete(Object* object) {
   DisplayOptionsPanel* this = (DisplayOptionsPanel*) object;
   FunctionBar_delete(this->decIncBar);
   Panel_done(&this->super);
   free(this);
}

function: DisplayOptionsPanel_eventHandler

function: DisplayOptionsPanel_new

struct: DisplayOptionsPanel

/* (Struct definition is typically in DisplayOptionsPanel.h) */
typedef struct DisplayOptionsPanel_ {
   Panel super;
   FunctionBar* decIncBar;
   Settings* settings;
   ScreenManager* scr;
} DisplayOptionsPanel;

struct instance: DisplayOptionsPanel_class

const PanelClass DisplayOptionsPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = DisplayOptionsPanel_delete
   },
   .eventHandler = DisplayOptionsPanel_eventHandler
};

DisplayOptionsPanel.h

DisplayOptionsPanel.h

extern const: DisplayOptionsPanel_class

extern const PanelClass DisplayOptionsPanel_class;

function: DisplayOptionsPanel_new

DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);

struct: DisplayOptionsPanel_

typedef struct DisplayOptionsPanel_ {
   Panel super;

   Settings* settings;
   ScreenManager* scr;
   FunctionBar* decIncBar;
} DisplayOptionsPanel;

type alias: DisplayOptionsPanel

typedef struct DisplayOptionsPanel_ {
   Panel super;

   Settings* settings;
   ScreenManager* scr;
   FunctionBar* decIncBar;
} DisplayOptionsPanel;

styleguide.md

docs/styleguide.md

function: BlankLinesInFunctionExample

   int stuff = 0;

   // If asked for gives only half the answer ...
   if (param)
      stuff = 21;

   // Compute the answer
   stuff %= 2;
   stuff *= 4;
   stuff *= 5;
   stuff += !!stuff;
   stuff *= 2;

   return stuff;

function: ComplexIfStatementWithBracesExample

if ((fd = open(filename, O_RDONLY)) >= 0 &&
   (amtRead = read(buffer, sizeof(buffer))) > 0) {
   // Parse the information further ...
   metric = handleBufferContent(buffer, amtRead);
} else {
   metric = -1;
}

if (fd >= 0)
   close(fd);

function: ControlFlowNewLinesNoBracesExample

if (answer)
   return 42;
else if (again)
   continue;
else
   break;

function: MultiLineIfStatementIndentationExample

if (very_long_condition &&
   another_very_complex_expression &&
   something_else_to_check) {
   // Code follows as normal ...
} else {

}

function: SimpleIfStatementNoBracesExample

if (answer)
   return 42;

struct: HeaderGuardExample

#ifndef HEADER_FILENAME
#define HEADER_FILENAME
/*
htop - Filename.h
(C) 2021 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

DragonFlyBSDMachine.c

dragonflybsd/DragonFlyBSDMachine.c

function: DragonFlyBSDMachine_readJailName

char* DragonFlyBSDMachine_readJailName(const DragonFlyBSDMachine* host, int jailid) {
   char* hostname;
   char* jname;

   if (jailid != 0 && host->jails && (hostname = (char*)Hashtable_get(host->jails, jailid))) {
      jname = xStrdup(hostname);
   } else {
      jname = xStrdup("-");
   }

   return jname;
}

function: DragonFlyBSDMachine_scanCPUTime

function: DragonFlyBSDMachine_scanJails

static void DragonFlyBSDMachine_scanJails(DragonFlyBSDMachine* this) {
   size_t len;
   char* jails; /* Jail list */
   char* curpos;
   char* nextpos;

   if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
      CRT_fatalError("initial sysctlbyname / jail.list failed");
   }

retry:
   if (len == 0)
      return;

   jails = xMalloc(len);

   if (sysctlbyname("jail.list", jails, &len, NULL, 0) == -1) {
      if (errno == ENOMEM) {
         free(jails);
         goto retry;
      }
      CRT_fatalError("sysctlbyname / jail.list failed");
   }

   if (this->jails) {
      Hashtable_delete(this->jails);
   }

   this->jails = Hashtable_new(20, true);
   curpos = jails;
   while (curpos) {
      int jailid;
      char* str_hostname;

      nextpos = strchr(curpos, '\n');
      if (nextpos) {
         *nextpos++ = 0;
      }

      jailid = atoi(strtok(curpos, " "));
      str_hostname = strtok(NULL, " ");

      char* jname = (char*) (Hashtable_get(this->jails, jailid));
      if (jname == NULL) {
         jname = xStrdup(str_hostname);
         Hashtable_put(this->jails, jailid, jname);
      }

      curpos = nextpos;
   }

   free(jails);
}

function: DragonFlyBSDMachine_scanMemoryInfo

static void DragonFlyBSDMachine_scanMemoryInfo(Machine* super) {
   DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super;

   // @etosan:
   // memory counter relationships seem to be these:
   //  total = active + wired + inactive + cache + free
   //  htop_used (unavail to anybody) = active + wired
   //  htop_cache (for cache meter)   = buffers + cache
   //  user_free (avail to procs)     = buffers + inactive + cache + free
   size_t len = sizeof(super->totalMem);

   //disabled for now, as it is always smaller than phycal amount of memory...
   //...to avoid "where is my memory?" questions
   //sysctl(MIB_vm_stats_vm_v_page_count, 4, &(this->totalMem), &len, NULL, 0);
   //this->totalMem *= pageSizeKb;
   sysctl(MIB_hw_physmem, 2, &(super->totalMem), &len, NULL, 0);
   super->totalMem /= 1024;

   unsigned long long int memActive = 0;
   sysctl(MIB_vm_stats_vm_v_active_count, 4, &memActive, &len, NULL, 0);
   memActive *= this->pageSizeKb;

   unsigned long long int memWire = 0;
   sysctl(MIB_vm_stats_vm_v_wire_count, 4, &memWire, &len, NULL, 0);
   memWire *= this->pageSizeKb;

   sysctl(MIB_vfs_bufspace, 2, &(super->buffersMem), &len, NULL, 0);
   super->buffersMem /= 1024;

   sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(super->cachedMem), &len, NULL, 0);
   super->cachedMem *= this->pageSizeKb;
   super->usedMem = memActive + memWire;

   struct kvm_swap swap[16];
   int nswap = kvm_getswapinfo(this->kd, swap, ARRAYSIZE(swap), 0);
   super->totalSwap = 0;
   super->usedSwap = 0;
   for (int i = 0; i < nswap; i++) {
      super->totalSwap += swap[i].ksw_total;
      super->usedSwap += swap[i].ksw_used;
   }
   super->totalSwap *= this->pageSizeKb;
   super->usedSwap *= this->pageSizeKb;
}

function: Machine_delete

void Machine_delete(Machine* super) {
   DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super;

   Machine_done(super);

   if (this->kd) {
      kvm_close(this->kd);
   }

   if (this->jails) {
      Hashtable_delete(this->jails);
   }

   free(this->cp_time_o);
   free(this->cp_time_n);
   free(this->cp_times_o);
   free(this->cp_times_n);
   free(this->cpus);

   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* host, unsigned int id) {
   assert(id < host->existingCPUs);
   (void)host; (void)id;

   // TODO: Support detecting online / offline CPUs.
   return true;
}

function: Machine_new

function: Machine_scan

void Machine_scan(Machine* super) {
   DragonFlyBSDMachine* this = (DragonFlyBSDMachine*) super;

   DragonFlyBSDMachine_scanMemoryInfo(super);
   DragonFlyBSDMachine_scanCPUTime(super);
   DragonFlyBSDMachine_scanJails(this);
}

DragonFlyBSDMachine.h

dragonflybsd/DragonFlyBSDMachine.h

function: DragonFlyBSDMachine_readJailName

char* DragonFlyBSDMachine_readJailName(const DragonFlyBSDMachine* host, int jailid);

struct: CPUData

typedef struct CPUData_ {
   double userPercent;
   double nicePercent;
   double systemPercent;
   double irqPercent;
   double idlePercent;
   double systemAllPercent;
} CPUData;

struct: DragonFlyBSDMachine

typedef struct DragonFlyBSDMachine_ {
   Machine super;
   kvm_t* kd;

   Hashtable* jails;

   int pageSize;
   int pageSizeKb;
   int kernelFScale;

   CPUData* cpus;

   unsigned long* cp_time_o;
   unsigned long* cp_time_n;

   unsigned long* cp_times_o;
   unsigned long* cp_times_n;
} DragonFlyBSDMachine;

DragonFlyBSDProcess.c

dragonflybsd/DragonFlyBSDProcess.c

function: DragonFlyBSDProcess_new

Process* DragonFlyBSDProcess_new(const Machine* host) {
   DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess));
   Object_setClass(this, Class(DragonFlyBSDProcess));
   Process_init(&this->super, host);
   return (Process*)this;
}

method: DragonFlyBSDProcess_compareByKey

static int DragonFlyBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const DragonFlyBSDProcess* p1 = (const DragonFlyBSDProcess*)v1;
   const DragonFlyBSDProcess* p2 = (const DragonFlyBSDProcess*)v2;

   switch (key) {
   // add Platform-specific fields here
   case JID:
      return SPACESHIP_NUMBER(p1->jid, p2->jid);
   case JAIL:
      return SPACESHIP_NULLSTR(p1->jname, p2->jname);
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

method: DragonFlyBSDProcess_rowWriteField

static void DragonFlyBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const Process* this = (const Process*) super;
   const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) super;

   char buffer[256]; buffer[255] = '\0';
   int attr = CRT_colors[DEFAULT_COLOR];
   size_t n = sizeof(buffer) - 1;

   switch (field) {
   // add Platform-specific fields here
   case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_isKernelThread(this) ? -1 : Process_getPid(this)); break;
   case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;
   case JAIL: Row_printLeftAlignedField(str, attr, fp->jname, 11); return;
   default:
      Process_writeField(&fp->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

method: Process_delete

void Process_delete(Object* cast) {
   DragonFlyBSDProcess* this = (DragonFlyBSDProcess*) cast;
   Process_done((Process*)cast);
   free(this->jname);
   free(this);
}

structure: DragonFlyBSDProcess_class

const ProcessClass DragonFlyBSDProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = DragonFlyBSDProcess_rowWriteField
   },
   .compareByKey = DragonFlyBSDProcess_compareByKey
};

structure: Process_fields

DragonFlyBSDProcess.h

dragonflybsd/DragonFlyBSDProcess.h

function: DragonFlyBSDProcess_new

Process* DragonFlyBSDProcess_new(const Machine* host);

function: Process_delete

void Process_delete(Object* cast);

struct: DragonFlyBSDProcess_

typedef struct DragonFlyBSDProcess_ {
   Process super;
   int   jid;
   char* jname;
} DragonFlyBSDProcess;

variable: DragonFlyBSDProcess_class

extern const ProcessClass DragonFlyBSDProcess_class;

variable: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

DragonFlyBSDProcessTable.c

dragonflybsd/DragonFlyBSDProcessTable.c

function: DragonFlyBSDProcessTable_updateCwd

static void DragonFlyBSDProcessTable_updateCwd(const struct kinfo_proc* kproc, Process* proc) {
   const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, kproc->kp_pid };
   char buffer[2048];
   size_t size = sizeof(buffer);
   if (sysctl(mib, 4, buffer, &size, NULL, 0) != 0) {
      free(proc->procCwd);
      proc->procCwd = NULL;
      return;
   }

   /* Kernel threads return an empty buffer */
   if (buffer[0] == '\0') {
      free(proc->procCwd);
      proc->procCwd = NULL;
      return;
   }

   free_and_xStrdup(&proc->procCwd, buffer);
}

function: DragonFlyBSDProcessTable_updateExe

static void DragonFlyBSDProcessTable_updateExe(const struct kinfo_proc* kproc, Process* proc) {
   if (Process_isKernelThread(proc))
      return;

   char path[32];
   xSnprintf(path, sizeof(path), "/proc/%d/file", kproc->kp_pid);

   char target[PATH_MAX];
   ssize_t ret = readlink(path, target, sizeof(target) - 1);
   if (ret <= 0)
      return;

   target[ret] = '\0';
   Process_updateExe(proc, target);
}

function: DragonFlyBSDProcessTable_updateProcessName

static void DragonFlyBSDProcessTable_updateProcessName(kvm_t* kd, const struct kinfo_proc* kproc, Process* proc) {
   Process_updateComm(proc, kproc->kp_comm);

   char** argv = kvm_getargv(kd, kproc, 0);
   if (!argv || !argv[0]) {
      Process_updateCmdline(proc, kproc->kp_comm, 0, strlen(kproc->kp_comm));
      return;
   }

   size_t len = 0;
   for (int i = 0; argv[i]; i++) {
      len += strlen(argv[i]) + 1;
   }

   char* cmdline = xMalloc(len);

   char* at = cmdline;
   int end = 0;
   for (int i = 0; argv[i]; i++) {
      at = stpcpy(at, argv[i]);
      if (end == 0) {
         end = at - cmdline;
      }
      *at++ = ' ';
   }
   at--;
   *at = '\0';

   Process_updateCmdline(proc, cmdline, 0, end);

   free(cmdline);
}

function: ProcessTable_delete

void ProcessTable_delete(Object* cast) {
   DragonFlyBSDProcessTable* this = (DragonFlyBSDProcessTable*) cast;
   ProcessTable_done(&this->super);
   free(this);
}

function: ProcessTable_goThroughEntries

function: ProcessTable_new

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
   DragonFlyBSDProcessTable* this = xCalloc(1, sizeof(DragonFlyBSDProcessTable));
   Object_setClass(this, Class(ProcessTable));

   ProcessTable* super = (ProcessTable*) this;
   ProcessTable_init(super, Class(DragonFlyBSDProcess), host, pidMatchList);

   return super;
}

DragonFlyBSDProcessTable.h

dragonflybsd/DragonFlyBSDProcessTable.h

struct: DragonFlyBSDProcessTable

typedef struct DragonFlyBSDProcessTable_ {
   ProcessTable super;
} DragonFlyBSDProcessTable;

Platform.c

dragonflybsd/Platform.c

file: Platform.c

Platform.h

dragonflybsd/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

macro: PLATFORM_LONG_OPTIONS

#define PLATFORM_LONG_OPTIONS

variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

variable: Platform_signals

extern const SignalItem Platform_signals[];

ProcessField.h

dragonflybsd/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   JID = 100,                    \
   JAIL = 101,                   \
                                 \
   DUMMY_BUMP_FIELD = CWD,       \
   // End of list

DynamicColumn.c

DynamicColumn.c

function: DynamicColumn_compare

static void DynamicColumn_compare(ht_key_t key, void* value, void* data) {
   const DynamicColumn* column = (const DynamicColumn*)value;
   DynamicIterator* iter = (DynamicIterator*)data;
   if (String_eq(iter->name, column->name)) {
      iter->data = column;
      iter->key = key;
   }
}

function: DynamicColumn_done

void DynamicColumn_done(DynamicColumn* this) {
   free(this->heading);
   free(this->caption);
   free(this->description);
}

function: DynamicColumn_lookup

const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key) {
   return (const DynamicColumn*) Hashtable_get(dynamics, key);
}

function: DynamicColumn_name

const char* DynamicColumn_name(unsigned int key) {
   return Platform_dynamicColumnName(key);
}

function: DynamicColumn_search

const DynamicColumn* DynamicColumn_search(Hashtable* dynamics, const char* name, unsigned int* key) {
   DynamicIterator iter = { .key = 0, .data = NULL, .name = name };
   if (dynamics)
      Hashtable_foreach(dynamics, DynamicColumn_compare, &iter);
   if (key)
      *key = iter.key;
   return iter.data;
}

function: DynamicColumn_writeField

bool DynamicColumn_writeField(const Process* proc, RichString* str, unsigned int key) {
   return Platform_dynamicColumnWriteField(proc, str, key);
}

function: DynamicColumns_delete

void DynamicColumns_delete(Hashtable* dynamics) {
   if (dynamics) {
      Platform_dynamicColumnsDone(dynamics);
      Hashtable_delete(dynamics);
   }
}

function: DynamicColumns_new

Hashtable* DynamicColumns_new(void) {
   Hashtable* dynamics = Platform_dynamicColumns();
   if (!dynamics)
      dynamics = Hashtable_new(0, true);
   return dynamics;
}

struct: DynamicIterator

typedef struct {
   const char* name;
   const DynamicColumn* data;
   unsigned int key;
} DynamicIterator;

DynamicColumn.h

DynamicColumn.h

function: DynamicColumn_done

void DynamicColumn_done(DynamicColumn* this);

function: DynamicColumn_lookup

const DynamicColumn* DynamicColumn_lookup(Hashtable* dynamics, unsigned int key);

function: DynamicColumn_name

const char* DynamicColumn_name(unsigned int key);

function: DynamicColumn_search

const DynamicColumn* DynamicColumn_search(Hashtable* dynamics, const char* name, unsigned int* key);

function: DynamicColumn_writeField

bool DynamicColumn_writeField(const Process* proc, RichString* str, unsigned int key);

function: DynamicColumns_delete

void DynamicColumns_delete(Hashtable* dynamics);

function: DynamicColumns_new

Hashtable* DynamicColumns_new(void);

macro: DYNAMIC_DEFAULT_COLUMN_WIDTH

#define DYNAMIC_DEFAULT_COLUMN_WIDTH -5

macro: DYNAMIC_MAX_COLUMN_WIDTH

#define DYNAMIC_MAX_COLUMN_WIDTH 64

struct: DynamicColumn

typedef struct DynamicColumn_ {
   char name[32];           /* unique, internal-only name */
   char* heading;           /* displayed in main screen */
   char* caption;           /* displayed in setup menu (short name) */
   char* description;       /* displayed in setup menu (detail) */
   int width;               /* display width +/- for value alignment */
   bool enabled;            /* false == ignore this column (until enabled) */
   Table* table;            /* pointer to DynamicScreen or ProcessTable */
} DynamicColumn;

DynamicMeter.c

DynamicMeter.c

function: DynamicMeter_compare

static void DynamicMeter_compare(ht_key_t key, void* value, void* data) {
   const DynamicMeter* meter = (const DynamicMeter*)value;
   DynamicIterator* iter = (DynamicIterator*)data;
   if (String_eq(iter->name, meter->name)) {
      iter->found = true;
      iter->key = key;
   }
}

function: DynamicMeter_display

static void DynamicMeter_display(const Object* cast, RichString* out) {
   const Meter* meter = (const Meter*)cast;
   Platform_dynamicMeterDisplay(meter, out);
}

function: DynamicMeter_getCaption

static const char* DynamicMeter_getCaption(const Meter* this) {
   const Settings* settings = this->host->settings;
   const DynamicMeter* meter = Hashtable_get(settings->dynamicMeters, this->param);
   if (meter)
      return meter->caption ? meter->caption : meter->name;
   return this->caption;
}

function: DynamicMeter_getUiName

static void DynamicMeter_getUiName(const Meter* this, char* name, size_t length) {
   assert(length > 0);

   const Settings* settings = this->host->settings;
   const DynamicMeter* meter = Hashtable_get(settings->dynamicMeters, this->param);
   if (meter) {
      const char* uiName = meter->caption;
      if (uiName) {
         size_t uiNameLen = strlen(uiName);
         if (uiNameLen > 2 && uiName[uiNameLen - 2] == ':')
            uiNameLen -= 2;

         String_safeStrncpy(name, uiName, MINIMUM(length, uiNameLen + 1));
      } else {
         String_safeStrncpy(name, meter->name, length);
      }
   }
}

function: DynamicMeter_init

static void DynamicMeter_init(Meter* meter) {
   Platform_dynamicMeterInit(meter);
}

function: DynamicMeter_lookup

const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key) {
   const DynamicMeter* meter = Hashtable_get(dynamics, key);
   return meter ? meter->name : NULL;
}

function: DynamicMeter_search

bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key) {
   DynamicIterator iter = { .key = 0, .name = name, .found = false };
   if (dynamics)
      Hashtable_foreach(dynamics, DynamicMeter_compare, &iter);
   if (key)
      *key = iter.key;
   return iter.found;
}

function: DynamicMeter_updateValues

static void DynamicMeter_updateValues(Meter* meter) {
   Platform_dynamicMeterUpdateValues(meter);
}

function: DynamicMeters_delete

void DynamicMeters_delete(Hashtable* dynamics) {
   if (dynamics) {
      Platform_dynamicMetersDone(dynamics);
      Hashtable_delete(dynamics);
   }
}

function: DynamicMeters_new

Hashtable* DynamicMeters_new(void) {
   return Platform_dynamicMeters();
}

global variable: DynamicMeter_attributes

static const int DynamicMeter_attributes[] = {
   DYNAMIC_GRAY,
   DYNAMIC_DARKGRAY,
   DYNAMIC_RED,
   DYNAMIC_GREEN,
   DYNAMIC_BLUE,
   DYNAMIC_CYAN,
   DYNAMIC_MAGENTA,
   DYNAMIC_YELLOW,
   DYNAMIC_WHITE
};

global variable: DynamicMeter_class

const MeterClass DynamicMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = DynamicMeter_display
   },
   .init = DynamicMeter_init,
   .updateValues = DynamicMeter_updateValues,
   .getCaption = DynamicMeter_getCaption,
   .getUiName = DynamicMeter_getUiName,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 0,
   .total = 100.0,
   .attributes = DynamicMeter_attributes,
   .name = "Dynamic",
   .uiName = "Dynamic",
   .caption = "",
};

struct: DynamicIterator

typedef struct {
   unsigned int key;
   const char* name;
   bool found;
} DynamicIterator;

DynamicMeter.h

DynamicMeter.h

function: DynamicMeter_lookup

const char* DynamicMeter_lookup(Hashtable* dynamics, unsigned int key);

function: DynamicMeter_search

bool DynamicMeter_search(Hashtable* dynamics, const char* name, unsigned int* key);

function: DynamicMeters_delete

void DynamicMeters_delete(Hashtable* dynamics);

function: DynamicMeters_new

Hashtable* DynamicMeters_new(void);

struct: DynamicMeter

typedef struct DynamicMeter_ {
   char name[32];  /* unique name, cannot contain spaces */
   char* caption;
   char* description;
   unsigned int type;
   double maximum;
} DynamicMeter;

variable: DynamicMeter_class

extern const MeterClass DynamicMeter_class;

DynamicScreen.c

DynamicScreen.c

function: DynamicScreen_compare

static void DynamicScreen_compare(ht_key_t key, void* value, void* data) {
   const DynamicScreen* screen = (const DynamicScreen*)value;
   DynamicIterator* iter = (DynamicIterator*)data;
   if (String_eq(iter->name, screen->name)) {
      iter->found = true;
      iter->key = key;
   }
}

function: DynamicScreen_done

void DynamicScreen_done(DynamicScreen* this) {
   free(this->caption);
   free(this->fields);
   free(this->heading);
   free(this->sortKey);
   free(this->columnKeys);
}

function: DynamicScreen_lookup

const char* DynamicScreen_lookup(Hashtable* screens, ht_key_t key) {
   const DynamicScreen* screen = Hashtable_get(screens, key);
   return screen ? screen->name : NULL;
}

function: DynamicScreen_search

bool DynamicScreen_search(Hashtable* screens, const char* name, ht_key_t* key) {
   DynamicIterator iter = { .key = 0, .name = name, .found = false };
   if (screens)
      Hashtable_foreach(screens, DynamicScreen_compare, &iter);
   if (key)
      *key = iter.key;
   return iter.found;
}

function: DynamicScreens_delete

void DynamicScreens_delete(Hashtable* screens) {
   if (screens) {
      Platform_dynamicScreensDone(screens);
      Hashtable_delete(screens);
   }
}

function: DynamicScreens_new

Hashtable* DynamicScreens_new(void) {
   return Platform_dynamicScreens();
}

struct: DynamicIterator

typedef struct {
   ht_key_t key;
   const char* name;
   bool found;
} DynamicIterator;

DynamicScreen.h

DynamicScreen.h

function: DynamicScreen_done

void DynamicScreen_done(DynamicScreen* this);

function: DynamicScreen_lookup

const char* DynamicScreen_lookup(Hashtable* screens, unsigned int key);

function: DynamicScreen_search

bool DynamicScreen_search(Hashtable* screens, const char* name, unsigned int* key);

function: DynamicScreens_addAvailableColumns

void DynamicScreens_addAvailableColumns(Panel* availableColumns, char* screen);

function: DynamicScreens_delete

void DynamicScreens_delete(Hashtable* screens);

function: DynamicScreens_new

Hashtable* DynamicScreens_new(void);

struct: DynamicScreen_

typedef struct DynamicScreen_ {
   char name[32];  /* unique name cannot contain any spaces */
   char* heading;  /* user-settable more readable name */
   char* caption;  /* explanatory text for screen */
   char* fields;
   char* sortKey;
   char* columnKeys;
   int direction;
} DynamicScreen;

EnvScreen.c

EnvScreen.c

function: EnvScreen_delete

void EnvScreen_delete(Object* this) {
   free(InfoScreen_done((InfoScreen*)this));
}

function: EnvScreen_draw

static void EnvScreen_draw(InfoScreen* this) {
   InfoScreen_drawTitled(this, "Environment of process %d - %s", Process_getPid(this->process), Process_getCommand(this->process));
}

function: EnvScreen_new

EnvScreen* EnvScreen_new(Process* process) {
   EnvScreen* this = xMalloc(sizeof(EnvScreen));
   Object_setClass(this, Class(EnvScreen));
   return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ");
}

function: EnvScreen_scan

static void EnvScreen_scan(InfoScreen* this) {
   Panel* panel = this->display;
   int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);

   Panel_prune(panel);

   char* env = Platform_getProcessEnv(Process_getPid(this->process));
   if (env) {
      for (const char* p = env; *p; p = strrchr(p, 0) + 1)
         InfoScreen_addLine(this, p);
      free(env);
   }
   else {
      InfoScreen_addLine(this, "Could not read process environment.");
   }

   Vector_insertionSort(this->lines);
   Vector_insertionSort(panel->items);
   Panel_setSelected(panel, idx);
}

struct: EnvScreen_class

const InfoScreenClass EnvScreen_class = {
   .super = {
      .extends = Class(Object),
      .delete = EnvScreen_delete
   },
   .scan = EnvScreen_scan,
   .draw = EnvScreen_draw
};

EnvScreen.h

EnvScreen.h

extern const: EnvScreen_class

extern const InfoScreenClass EnvScreen_class;

function: EnvScreen_delete

void EnvScreen_delete(Object* this);

function: EnvScreen_new

EnvScreen* EnvScreen_new(Process* process);

struct: EnvScreen_

typedef struct EnvScreen_ {
   InfoScreen super;
} EnvScreen;

type alias: EnvScreen

typedef struct EnvScreen_ {
   InfoScreen super;
} EnvScreen;

FileDescriptorMeter.c

FileDescriptorMeter.c

file: FileDescriptorMeter.c

FileDescriptorMeter.h

FileDescriptorMeter.h

struct: FileDescriptorMeter_class

extern const MeterClass FileDescriptorMeter_class;

FreeBSDMachine.c

freebsd/FreeBSDMachine.c

function: FreeBSDMachine_scanCPU

function: FreeBSDMachine_scanMemoryInfo

function: Machine_delete

void Machine_delete(Machine* super) {
   FreeBSDMachine* this = (FreeBSDMachine*) super;

   Machine_done(super);

   if (this->kd) {
      kvm_close(this->kd);
   }

   free(this->cp_time_o);
   free(this->cp_time_n);
   free(this->cp_times_o);
   free(this->cp_times_n);
   free(this->cpus);

   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* host, unsigned int id) {
   assert(id < host->existingCPUs);

   // TODO: support offline CPUs and hot swapping
   (void) host; (void) id;

   return true;
}

function: Machine_new

function: Machine_scan

void Machine_scan(Machine* super) {
   FreeBSDMachine* this = (FreeBSDMachine*) super;

   openzfs_sysctl_updateArcStats(&this->zfs);
   FreeBSDMachine_scanMemoryInfo(super);
   FreeBSDMachine_scanCPU(super);
}

FreeBSDMachine.h

freebsd/FreeBSDMachine.h

struct: CPUData

typedef struct CPUData_ {
   double userPercent;
   double nicePercent;
   double systemPercent;
   double irqPercent;
   double systemAllPercent;

   double frequency;
   double temperature;
} CPUData;

struct: FreeBSDMachine

typedef struct FreeBSDMachine_ {
   Machine super;
   kvm_t* kd;

   int pageSize;
   int pageSizeKb;
   int kernelFScale;

   ZfsArcStats zfs;

   CPUData* cpus;

   unsigned long* cp_time_o;
   unsigned long* cp_time_n;

   unsigned long* cp_times_o;
   unsigned long* cp_times_n;

} FreeBSDMachine;

FreeBSDProcess.c

freebsd/FreeBSDProcess.c

function: FreeBSDProcess_compareByKey

static int FreeBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const FreeBSDProcess* p1 = (const FreeBSDProcess*)v1;
   const FreeBSDProcess* p2 = (const FreeBSDProcess*)v2;

   switch (key) {
   // add FreeBSD-specific fields here
   case JID:
      return SPACESHIP_NUMBER(p1->jid, p2->jid);
   case JAIL:
      return SPACESHIP_NULLSTR(p1->jname, p2->jname);
   case EMULATION:
      return SPACESHIP_NULLSTR(p1->emul, p2->emul);
   case SCHEDCLASS:
      return SPACESHIP_NUMBER(p1->sched_class, p2->sched_class);
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

function: FreeBSDProcess_new

Process* FreeBSDProcess_new(const Machine* machine) {
   FreeBSDProcess* this = xCalloc(1, sizeof(FreeBSDProcess));
   Object_setClass(this, Class(FreeBSDProcess));
   Process_init(&this->super, machine);
   return (Process*)this;
}

function: FreeBSDProcess_rowWriteField

static void FreeBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const FreeBSDProcess* fp = (const FreeBSDProcess*) super;

   char buffer[256]; buffer[255] = '\0';
   char sched_class;
   int attr = CRT_colors[DEFAULT_COLOR];
   size_t n = sizeof(buffer) - 1;

   switch (field) {
   // add FreeBSD-specific fields here
   case JID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, fp->jid); break;

   case JAIL:
      Row_printLeftAlignedField(str, attr, fp->jname ? fp->jname : "N/A", 11);
      return;

   case EMULATION:
      Row_printLeftAlignedField(str, attr, fp->emul ? fp->emul : "N/A", 16);
      return;

   case SCHEDCLASS:
      assert(0 <= fp->sched_class && fp->sched_class < ARRAYSIZE(FreeBSD_schedclassChars));
      sched_class = FreeBSD_schedclassChars[fp->sched_class];
      assert(sched_class);
      xSnprintf(buffer, n, " %c", sched_class);
      break;

   default:
      Process_writeField(&fp->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

function: Process_delete

void Process_delete(Object* cast) {
   FreeBSDProcess* this = (FreeBSDProcess*) cast;
   Process_done((Process*)cast);
   free(this->emul);
   free(this->jname);
   free(this);
}

global variable: FreeBSD_schedclassChars

static const char FreeBSD_schedclassChars[MAX_SCHEDCLASS] = {
   [SCHEDCLASS_UNKNOWN] = '?',     // Something went wrong or the base system has a new scheduling class
   [SCHEDCLASS_INTR_THREAD] = '-', // interrupt thread, these have special handling of priority
   [SCHEDCLASS_IDLE] = 'i',        // idletime scheduling
   [SCHEDCLASS_TIMESHARE] = ' ',   // timesharing process scheduling (regular processes are timeshared)
   [SCHEDCLASS_REALTIME] = 'r',    // realtime scheduling
};

global variable: FreeBSDProcess_class

const ProcessClass FreeBSDProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = FreeBSDProcess_rowWriteField
   },
   .compareByKey = FreeBSDProcess_compareByKey
};

global variable: nodevStr

const char* const nodevStr = "nodev";

global variable: Process_fields

FreeBSDProcess.h

freebsd/FreeBSDProcess.h

enum: FreeBSDSchedClass

typedef enum {
   SCHEDCLASS_UNKNOWN = 0,

   SCHEDCLASS_INTR_THREAD, /* interrupt thread */
   SCHEDCLASS_REALTIME,
   SCHEDCLASS_TIMESHARE, /* Regular scheduling */
   SCHEDCLASS_IDLE,

   MAX_SCHEDCLASS,
} FreeBSDSchedClass;

function: FreeBSDProcess_new

Process* FreeBSDProcess_new(const Machine* host);

function: Process_delete

void Process_delete(Object* cast);

global constant: FreeBSDProcess_class

extern const ProcessClass FreeBSDProcess_class;

global constant: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

struct: FreeBSDProcess_

typedef struct FreeBSDProcess_ {
   Process super;
   int   jid;
   char* jname;
   char* emul;
   FreeBSDSchedClass sched_class;
} FreeBSDProcess;

type alias: FreeBSDProcess

typedef struct FreeBSDProcess_ {
   Process super;
   int   jid;
   char* jname;
   char* emul;
   FreeBSDSchedClass sched_class;
} FreeBSDProcess;

FreeBSDProcessTable.c

freebsd/FreeBSDProcessTable.c

file: FreeBSDProcessTable.c

FreeBSDProcessTable.h

freebsd/FreeBSDProcessTable.h

struct: FreeBSDProcessTable

typedef struct FreeBSDProcessTable_ {
   ProcessTable super;
} FreeBSDProcessTable;

Platform.c

freebsd/Platform.c

function: Platform_done

void Platform_done(void) {
   /* no platform-specific cleanup needed */
}

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC) {
   int life;
   size_t life_len = sizeof(life);
   if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
      *percent = NAN;
   else
      *percent = life;

   int acline;
   size_t acline_len = sizeof(acline);
   if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
      *isOnAC = AC_ERROR;
   else
      *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
}

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data) {

   if (devstat_checkversion(NULL) < 0)
      return false;

   // use static to plug memory leak; see #841
   static struct devinfo info = { 0 };
   struct statinfo current = { .dinfo = &info };

   // get number of devices
   if (devstat_getdevs(NULL, &current) < 0)
      return false;

   int count = current.dinfo->numdevs;

   uint64_t bytesReadSum = 0, bytesWriteSum = 0, timeSpendSum = 0;
   uint64_t numDisks = 0;

   // get data
   for (int i = 0; i < count; i++) {
      uint64_t bytes_read, bytes_write;
      long double busy_time;

      devstat_compute_statistics(&current.dinfo->devices[i],
                                 NULL,
                                 1.0,
                                 DSM_TOTAL_BYTES_READ, &bytes_read,
                                 DSM_TOTAL_BYTES_WRITE, &bytes_write,
                                 DSM_TOTAL_BUSY_TIME, &busy_time,
                                 DSM_NONE);

      bytesReadSum += bytes_read;
      bytesWriteSum += bytes_write;
      timeSpendSum += 1000 * busy_time;
      numDisks++;
   }

   data->totalBytesRead = bytesReadSum;
   data->totalBytesWritten = bytesWriteSum;
   data->totalMsTimeSpend = timeSpendSum;
   data->numDisks = numDisks;
   return true;
}

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max) {
   Generic_getFileDescriptors_sysctl(used, max);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
   struct loadavg loadAverage;
   const int mib[2] = { CTL_VM, VM_LOADAVG };
   size_t size = sizeof(loadAverage);

   int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
   if (err) {
      *one = 0;
      *five = 0;
      *fifteen = 0;
      return;
   }
   *one     = (double) loadAverage.ldavg[0] / loadAverage.fscale;
   *five    = (double) loadAverage.ldavg[1] / loadAverage.fscale;
   *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void) {
   int maxPid;
   size_t size = sizeof(maxPid);
   int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0);
   if (err) {
      return 99999;
   }
   return maxPid;
}

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data) {
   // get number of interfaces
   int count;
   size_t countLen = sizeof(count);
   const int countMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT };

   int r = sysctl(countMib, ARRAYSIZE(countMib), &count, &countLen, NULL, 0);
   if (r < 0)
      return false;

   for (int i = 1; i <= count; i++) {
      struct ifmibdata ifmd;
      size_t ifmdLen = sizeof(ifmd);

      const int dataMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL };

      r = sysctl(dataMib, ARRAYSIZE(dataMib), &ifmd, &ifmdLen, NULL, 0);
      if (r < 0)
         continue;

      if (ifmd.ifmd_flags & IFF_LOOPBACK)
         continue;

      data->bytesReceived += ifmd.ifmd_data.ifi_ibytes;
      data->packetsReceived += ifmd.ifmd_data.ifi_ipackets;
      data->bytesTransmitted += ifmd.ifmd_data.ifi_obytes;
      data->packetsTransmitted += ifmd.ifmd_data.ifi_opackets;
   }

   return true;
}

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid) {
   const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ENV, pid };

   size_t capacity = ARG_MAX;
   char* env = xMalloc(capacity);

   int err = sysctl(mib, 4, env, &capacity, NULL, 0);
   if (err || capacity == 0) {
      free(env);
      return NULL;
   }

   if (env[capacity - 1] || env[capacity - 2]) {
      env = xRealloc(env, capacity + 2);
      env[capacity] = 0;
      env[capacity + 1] = 0;
   }

   return env;
}

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
   (void)pid;
   return NULL;
}

function: Platform_getUptime

int Platform_getUptime(void) {
   struct timeval bootTime, currTime;
   const int mib[2] = { CTL_KERN, KERN_BOOTTIME };
   size_t size = sizeof(bootTime);

   int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
   if (err) {
      return -1;
   }
   gettimeofday(&currTime, NULL);

   return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
}

function: Platform_init

bool Platform_init(void) {
   /* no platform-specific setup needed */
   return true;
}

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys) {
   /* no platform-specific key bindings */
   (void) keys;
}

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu) {
   const Machine* host = this->host;
   const FreeBSDMachine* fhost = (const FreeBSDMachine*) host;
   unsigned int cpus = host->activeCPUs;

   // single CPU box has everything in fhost->cpus[0]
   const CPUData* cpuData = cpus == 1 ? &fhost->cpus[0] : &fhost->cpus[cpu];

   double  percent;
   double* v = this->values;

   v[CPU_METER_NICE]   = cpuData->nicePercent;
   v[CPU_METER_NORMAL] = cpuData->userPercent;
   if (host->settings->detailedCPUTime) {
      v[CPU_METER_KERNEL]  = cpuData->systemPercent;
      v[CPU_METER_IRQ]     = cpuData->irqPercent;
      this->curItems = 4;
      percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ];
   } else {
      v[CPU_METER_KERNEL] = cpuData->systemAllPercent;
      this->curItems = 3;
      percent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL];
   }

   percent = CLAMP(percent, 0.0, 100.0);

   v[CPU_METER_FREQUENCY] = cpuData->frequency;
   v[CPU_METER_TEMPERATURE] = cpuData->temperature;

   return percent;
}

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this) {
   const Machine* host = this->host;
   const FreeBSDMachine* fhost = (const FreeBSDMachine*) host;

   this->total = host->totalMem;
   this->values[MEMORY_METER_USED] = host->usedMem;
   this->values[MEMORY_METER_SHARED] = host->sharedMem;
   // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux"
   this->values[MEMORY_METER_BUFFERS] = host->buffersMem;
   this->values[MEMORY_METER_CACHE] = host->cachedMem;
   // this->values[MEMORY_METER_AVAILABLE] = "available memory"

   if (fhost->zfs.enabled) {
      // ZFS does not shrink below the value of zfs_arc_min.
      unsigned long long int shrinkableSize = 0;
      if (fhost->zfs.size > fhost->zfs.min)
         shrinkableSize = fhost->zfs.size - fhost->zfs.min;
      this->values[MEMORY_METER_USED] -= shrinkableSize;
      this->values[MEMORY_METER_CACHE] += shrinkableSize;
      // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize;
   }
}

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this) {
   const Machine* host = this->host;

   this->total = host->totalSwap;
   this->values[SWAP_METER_USED] = host->usedSwap;
   // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux"
   // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux"
}

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this) {
   const FreeBSDMachine* fhost = (const FreeBSDMachine*) this->host;

   ZfsArcMeter_readStats(this, &fhost->zfs);
}

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this) {
   const FreeBSDMachine* fhost = (const FreeBSDMachine*) this->host;

   ZfsCompressedArcMeter_readStats(this, &fhost->zfs);
}

global variable: Platform_defaultScreens

const ScreenDefaults Platform_defaultScreens[] = {
   {
      .name = "Main",
      .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
      .sortKey = "PERCENT_CPU",
   },
};

global variable: Platform_meterTypes

const MeterClass* const Platform_meterTypes[] = {
   &CPUMeter_class,
   &ClockMeter_class,
   &DateMeter_class,
   &DateTimeMeter_class,
   &LoadAverageMeter_class,
   &LoadMeter_class,
   &MemoryMeter_class,
   &SwapMeter_class,
   &MemorySwapMeter_class,
   &TasksMeter_class,
   &UptimeMeter_class,
   &BatteryMeter_class,
   &HostnameMeter_class,
   &SysArchMeter_class,
   &AllCPUsMeter_class,
   &AllCPUs2Meter_class,
   &AllCPUs4Meter_class,
   &AllCPUs8Meter_class,
   &LeftCPUsMeter_class,
   &RightCPUsMeter_class,
   &LeftCPUs2Meter_class,
   &RightCPUs2Meter_class,
   &LeftCPUs4Meter_class,
   &RightCPUs4Meter_class,
   &LeftCPUs8Meter_class,
   &RightCPUs8Meter_class,
   &BlankMeter_class,
   &ZfsArcMeter_class,
   &ZfsCompressedArcMeter_class,
   &DiskIOMeter_class,
   &FileDescriptorMeter_class,
   &NetworkIOMeter_class,
   NULL
};

global variable: Platform_numberOfDefaultScreens

const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);

global variable: Platform_numberOfSignals

const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);

global variable: Platform_signals

const SignalItem Platform_signals[] = {
   { .name = " 0 Cancel",    .number =  0 },
   { .name = " 1 SIGHUP",    .number =  1 },
   { .name = " 2 SIGINT",    .number =  2 },
   { .name = " 3 SIGQUIT",   .number =  3 },
   { .name = " 4 SIGILL",    .number =  4 },
   { .name = " 5 SIGTRAP",   .number =  5 },
   { .name = " 6 SIGABRT",   .number =  6 },
   { .name = " 7 SIGEMT",    .number =  7 },
   { .name = " 8 SIGFPE",    .number =  8 },
   { .name = " 9 SIGKILL",   .number =  9 },
   { .name = "10 SIGBUS",    .number = 10 },
   { .name = "11 SIGSEGV",   .number = 11 },
   { .name = "12 SIGSYS",    .number = 12 },
   { .name = "13 SIGPIPE",   .number = 13 },
   { .name = "14 SIGALRM",   .number = 14 },
   { .name = "15 SIGTERM",   .number = 15 },
   { .name = "16 SIGURG",    .number = 16 },
   { .name = "17 SIGSTOP",   .number = 17 },
   { .name = "18 SIGTSTP",   .number = 18 },
   { .name = "19 SIGCONT",   .number = 19 },
   { .name = "20 SIGCHLD",   .number = 20 },
   { .name = "21 SIGTTIN",   .number = 21 },
   { .name = "22 SIGTTOU",   .number = 22 },
   { .name = "23 SIGIO",     .number = 23 },
   { .name = "24 SIGXCPU",   .number = 24 },
   { .name = "25 SIGXFSZ",   .number = 25 },
   { .name = "26 SIGVTALRM", .number = 26 },
   { .name = "27 SIGPROF",   .number = 27 },
   { .name = "28 SIGWINCH",  .number = 28 },
   { .name = "29 SIGINFO",   .number = 29 },
   { .name = "30 SIGUSR1",   .number = 30 },
   { .name = "31 SIGUSR2",   .number = 31 },
   { .name = "32 SIGTHR",    .number = 32 },
   { .name = "33 SIGLIBRT",  .number = 33 },
};

Platform.h

freebsd/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this);

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this);

global variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

global variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

global variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

global variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

global variable: Platform_signals

extern const SignalItem Platform_signals[];

macro: PLATFORM_LONG_OPTIONS

#define PLATFORM_LONG_OPTIONS

ProcessField.h

freebsd/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   JID = 100,                    \
   JAIL = 101,                   \
   EMULATION = 102,              \
   SCHEDCLASS = 103,             \
                                 \
   DUMMY_BUMP_FIELD = CWD,       \
   // End of list

FunctionBar.c

FunctionBar.c

function: FunctionBar_append

void FunctionBar_append(const char* buffer, int attr) {
   if (attr == -1) {
      attrset(CRT_colors[FUNCTION_BAR]);
   } else {
      attrset(attr);
   }
   mvaddstr(LINES - 1, currentLen + 1, buffer);
   attrset(CRT_colors[RESET_COLOR]);

   currentLen += strlen(buffer) + 1;
}

function: FunctionBar_delete

void FunctionBar_delete(FunctionBar* this) {
   for (int i = 0; i < FUNCTIONBAR_MAXEVENTS && this->functions[i]; i++) {
      free(this->functions[i]);
   }
   free(this->functions);
   if (!this->staticData) {
      for (int i = 0; i < this->size; i++) {
         free(this->keys.keys[i]);
      }
      free(this->keys.keys);
      free(this->events);
   }
   free(this);
}

function: FunctionBar_draw

int FunctionBar_draw(const FunctionBar* this) {
   return FunctionBar_drawExtra(this, NULL, -1, false);
}

function: FunctionBar_drawExtra

int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
   int cursorX = 0;
   attrset(CRT_colors[FUNCTION_BAR]);
   mvhline(LINES - 1, 0, ' ', COLS);
   int x = 0;
   for (int i = 0; i < this->size; i++) {
      attrset(CRT_colors[FUNCTION_KEY]);
      mvaddstr(LINES - 1, x, this->keys.constKeys[i]);
      x += strlen(this->keys.constKeys[i]);
      attrset(CRT_colors[FUNCTION_BAR]);
      mvaddstr(LINES - 1, x, this->functions[i]);
      x += strlen(this->functions[i]);
   }

   if (buffer) {
      if (attr == -1) {
         attrset(CRT_colors[FUNCTION_BAR]);
      } else {
         attrset(attr);
      } {
      mvaddstr(LINES - 1, x, buffer);
      x += strlen(buffer);
      cursorX = x;
   }

   attrset(CRT_colors[RESET_COLOR]);

   if (setCursor) {
      curs_set(1);
   } else {
      curs_set(0);
   }

   currentLen = x;

   return cursorX;
}

function: FunctionBar_new

FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events) {
   FunctionBar* this = xCalloc(1, sizeof(FunctionBar));
   this->functions = xCalloc(FUNCTIONBAR_MAXEVENTS + 1, sizeof(char*));
   if (!functions) {
      functions = FunctionBar_FLabels;
   }
   for (int i = 0; i < FUNCTIONBAR_MAXEVENTS && functions[i]; i++) {
      this->functions[i] = xStrdup(functions[i]);
   }
   if (keys && events) {
      this->staticData = false;
      this->keys.keys = xCalloc(FUNCTIONBAR_MAXEVENTS, sizeof(char*));
      this->events = xCalloc(FUNCTIONBAR_MAXEVENTS, sizeof(int));
      int i = 0;
      while (i < FUNCTIONBAR_MAXEVENTS && functions[i]) {
         this->keys.keys[i] = xStrdup(keys[i]);
         this->events[i] = events[i];
         i++;
      }
      this->size = i;
   } else {
      this->staticData = true;
      this->keys.constKeys = FunctionBar_FKeys;
      this->events = FunctionBar_FEvents;
      this->size = ARRAYSIZE(FunctionBar_FEvents);
   }
   return this;
}

function: FunctionBar_newEnterEsc

FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
   const char* functions[FUNCTIONBAR_MAXEVENTS + 1] = {enter, esc, NULL};
   return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
}

function: FunctionBar_setLabel

void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
   for (int i = 0; i < this->size; i++) {
      if (this->events[i] == event) {
         free(this->functions[i]);
         this->functions[i] = xStrdup(text);
         break;
      }
   }
}

function: FunctionBar_synthesizeEvent

int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {
   int x = 0;
   for (int i = 0; i < this->size; i++) {
      x += strlen(this->keys.constKeys[i]);
      x += strlen(this->functions[i]);
      if (pos < x) {
         return this->events[i];
      }
   }
   return ERR;
}

global_variable: currentLen

static int currentLen = 0;

global_variable: FunctionBar_EnterEscEvents

static const int FunctionBar_EnterEscEvents[] = {13, 27};

global_variable: FunctionBar_EnterEscKeys

static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};

global_variable: FunctionBar_FEvents

static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10)};

global_variable: FunctionBar_FKeys

static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};

global_variable: FunctionBar_FLabels

static const char* const FunctionBar_FLabels[] = {"      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", NULL};

FunctionBar.h

FunctionBar.h

function: FunctionBar_append

void FunctionBar_append(const char* buffer, int attr);

function: FunctionBar_new

FunctionBar* FunctionBar_new(const char* const* functions, const char* const* keys, const int* events);

function: FunctionBar_newEnterEsc

FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc);

method: FunctionBar_delete

void FunctionBar_delete(FunctionBar* this);

method: FunctionBar_draw

int FunctionBar_draw(const FunctionBar* this);

method: FunctionBar_drawExtra

int FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);

method: FunctionBar_setLabel

void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);

method: FunctionBar_synthesizeEvent

int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

struct: FunctionBar

typedef struct FunctionBar_ {
   int size;
   char** functions;
   union {
      char** keys;
      const char* const* constKeys;
   } keys;
   int* events;
   bool staticData;
} FunctionBar;

fdstat_sysctl.c

generic/fdstat_sysctl.c

function: Generic_getFileDescriptors_sysctl

void Generic_getFileDescriptors_sysctl(double* used, double* max) {
#if defined(HTOP_DARWIN)
   Generic_getFileDescriptors_sysctl_internal(
      "kern.maxfiles", "kern.num_files", 0, 0, used, max);
#elif defined(HTOP_DRAGONFLYBSD)
   Generic_getFileDescriptors_sysctl_internal(
      "kern.maxfiles", "kern.openfiles", 0, 0, used, max);
#elif defined(HTOP_FREEBSD)
   Generic_getFileDescriptors_sysctl_internal(
      "kern.maxfiles", "kern.openfiles", 0, 0, used, max);
#elif defined(HTOP_NETBSD)
   Generic_getFileDescriptors_sysctl_internal(
      "kern.maxfiles", NULL, 0, sizeof(struct kinfo_file), used, max);
#else
#error Unknown platform: Please implement proper way to query open/max file information
#endif
}

function: Generic_getFileDescriptors_sysctl_internal

static void Generic_getFileDescriptors_sysctl_internal(
   const char* sysctlname_maxfiles,
   const char* sysctlname_numfiles,
   size_t size_header,
   size_t size_entry,
   double* used,
   double* max
) {
   *used = NAN;
   *max = 65536;

   int max_fd, open_fd;
   size_t len;

   len = sizeof(max_fd);
   if (sysctlname_maxfiles && sysctlbyname(sysctlname_maxfiles, &max_fd, &len, NULL, 0) == 0) {
      if (max_fd) {
         *max = max_fd;
      } else {
         *max = NAN;
      }
   }

   len = sizeof(open_fd);
   if (sysctlname_numfiles && sysctlbyname(sysctlname_numfiles, &open_fd, &len, NULL, 0) == 0) {
      *used = open_fd;
      return;
   }

   // If no sysctl arc available, try to guess from the file table size at kern.file
   // The size per entry differs per OS, thus skip if we don't know:
   if (!size_entry)
      return;

   len = 0;
   if (sysctlbyname("kern.file", NULL, &len, NULL, 0) < 0)
      return;

   if (len < size_header)
      return;

   *used = (len - size_header) / size_entry;
}

fdstat_sysctl.h

generic/fdstat_sysctl.h

function: Generic_getFileDescriptors_sysctl

void Generic_getFileDescriptors_sysctl(double* used, double* max);

gettime.c

generic/gettime.c

function: Generic_gettime_monotonic

void Generic_gettime_monotonic(uint64_t* msec) {
#if defined(HAVE_CLOCK_GETTIME)

   struct timespec ts;
   if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
      *msec = ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
   else
      *msec = 0;

#else /* lower resolution gettimeofday() should be always available */

   struct timeval tv;
   if (gettimeofday(&tv, NULL) == 0)
      *msec = ((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000);
   else
      *msec = 0;

#endif
}

function: Generic_gettime_realtime

void Generic_gettime_realtime(struct timeval* tvp, uint64_t* msec) {

#if defined(HAVE_CLOCK_GETTIME)

   struct timespec ts;
   if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
      tvp->tv_sec = ts.tv_sec;
      tvp->tv_usec = ts.tv_nsec / 1000;
      *msec = ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
   } else {
      memset(tvp, 0, sizeof(struct timeval));
      *msec = 0;
   }

#else /* lower resolution gettimeofday(2) is always available */

   struct timeval tv;
   if (gettimeofday(&tv, NULL) == 0) {
      *tvp = tv; /* struct copy */
      *msec = ((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000);
   } else {
      memset(tvp, 0, sizeof(struct timeval));
      *msec = 0;
   }

#endif
}

gettime.h

generic/gettime.h

function: Generic_gettime_monotonic

void Generic_gettime_monotonic(uint64_t* msec);

function: Generic_gettime_realtime

void Generic_gettime_realtime(struct timeval* tvp, uint64_t* msec);

hostname.c

generic/hostname.c

function: Generic_hostname

void Generic_hostname(char* buffer, size_t size) {
   gethostname(buffer, size - 1);
   buffer[size - 1] = '\0';
}

hostname.h

generic/hostname.h

function: Generic_hostname

void Generic_hostname(char* buffer, size_t size);

openzfs_sysctl.c

generic/openzfs_sysctl.c

function: openzfs_sysctl_init

void openzfs_sysctl_init(ZfsArcStats* stats) {
   size_t len;
   unsigned long long int arcSize;

   len = sizeof(arcSize);
   if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arcSize, &len, NULL, 0) == 0 && arcSize != 0) {
      stats->enabled = 1;

      len = 5;
      sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);

      sysctlnametomib("kstat.zfs.misc.arcstats.c_min", MIB_kstat_zfs_misc_arcstats_c_min, &len);
      sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len);
      sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len);
      sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len);
      sysctlnametomib("kstat.zfs.misc.arcstats.anon_size", MIB_kstat_zfs_misc_arcstats_anon_size, &len);
      sysctlnametomib("kstat.zfs.misc.arcstats.hdr_size", MIB_kstat_zfs_misc_arcstats_hdr_size, &len);
      sysctlnametomib("kstat.zfs.misc.arcstats.other_size", MIB_kstat_zfs_misc_arcstats_other_size, &len);

      if (sysctlnametomib("kstat.zfs.misc.arcstats.compressed_size", MIB_kstat_zfs_misc_arcstats_compressed_size, &len) == 0) {
         stats->isCompressed = 1;
         sysctlnametomib("kstat.zfs.misc.arcstats.uncompressed_size", MIB_kstat_zfs_misc_arcstats_uncompressed_size, &len);
      } else {
         stats->isCompressed = 0;
      }
   } else {
      stats->enabled = 0;
   }
}

function: openzfs_sysctl_updateArcStats

void openzfs_sysctl_updateArcStats(ZfsArcStats* stats) {
   size_t len;

   if (stats->enabled) {
      len = sizeof(stats->size);
      sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0);
      stats->size /= 1024;

      len = sizeof(stats->min);
      sysctl(MIB_kstat_zfs_misc_arcstats_c_min, 5, &(stats->min), &len, NULL, 0);
      stats->min /= 1024;

      len = sizeof(stats->max);
      sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0);
      stats->max /= 1024;

      len = sizeof(stats->MFU);
      sysctl(MIB_kstat_zfs_misc_arcstats_mfu_size, 5, &(stats->MFU), &len, NULL, 0);
      stats->MFU /= 1024;

      len = sizeof(stats->MRU);
      sysctl(MIB_kstat_zfs_misc_arcstats_mru_size, 5, &(stats->MRU), &len, NULL, 0);
      stats->MRU /= 1024;

      len = sizeof(stats->anon);
      sysctl(MIB_kstat_zfs_misc_arcstats_anon_size, 5, &(stats->anon), &len, NULL, 0);
      stats->anon /= 1024;

      len = sizeof(stats->header);
      sysctl(MIB_kstat_zfs_misc_arcstats_hdr_size, 5, &(stats->header), &len, NULL, 0);
      stats->header /= 1024;

      len = sizeof(stats->other);
      sysctl(MIB_kstat_zfs_misc_arcstats_other_size, 5, &(stats->other), &len, NULL, 0);
      stats->other /= 1024;

      if (stats->isCompressed) {
         len = sizeof(stats->compressed);
         sysctl(MIB_kstat_zfs_misc_arcstats_compressed_size, 5, &(stats->compressed), &len, NULL, 0);
         stats->compressed /= 1024;

         len = sizeof(stats->uncompressed);
         sysctl(MIB_kstat_zfs_misc_arcstats_uncompressed_size, 5, &(stats->uncompressed), &len, NULL, 0);
         stats->uncompressed /= 1024;
      }
   }
}

variable: MIB_kstat_zfs_misc_arcstats_anon_size

static int MIB_kstat_zfs_misc_arcstats_anon_size[5];

variable: MIB_kstat_zfs_misc_arcstats_c_max

static int MIB_kstat_zfs_misc_arcstats_c_max[5];

variable: MIB_kstat_zfs_misc_arcstats_c_min

static int MIB_kstat_zfs_misc_arcstats_c_min[5];

variable: MIB_kstat_zfs_misc_arcstats_compressed_size

static int MIB_kstat_zfs_misc_arcstats_compressed_size[5];

variable: MIB_kstat_zfs_misc_arcstats_hdr_size

static int MIB_kstat_zfs_misc_arcstats_hdr_size[5];

variable: MIB_kstat_zfs_misc_arcstats_mfu_size

static int MIB_kstat_zfs_misc_arcstats_mfu_size[5];

variable: MIB_kstat_zfs_misc_arcstats_mru_size

static int MIB_kstat_zfs_misc_arcstats_mru_size[5];

variable: MIB_kstat_zfs_misc_arcstats_other_size

static int MIB_kstat_zfs_misc_arcstats_other_size[5];

variable: MIB_kstat_zfs_misc_arcstats_size

static int MIB_kstat_zfs_misc_arcstats_size[5];

variable: MIB_kstat_zfs_misc_arcstats_uncompressed_size

static int MIB_kstat_zfs_misc_arcstats_uncompressed_size[5];

openzfs_sysctl.h

generic/openzfs_sysctl.h

function: openzfs_sysctl_init

void openzfs_sysctl_init(ZfsArcStats* stats);

function: openzfs_sysctl_updateArcStats

void openzfs_sysctl_updateArcStats(ZfsArcStats* stats);

uname.c

generic/uname.c

function: Generic_uname

char* Generic_uname(void) {
   static char savedString[
      /* uname structure fields - manpages recommend sizeof */
      sizeof(((struct utsname*)0)->sysname) +
      sizeof(((struct utsname*)0)->release) +
      sizeof(((struct utsname*)0)->machine) +
      16/*markup*/ +
      128/*distro*/] = {'\0'};
   static bool loaded_data = false;

   if (!loaded_data) {
      struct utsname uname_info;
      int uname_result = uname(&uname_info);

      char distro[128];
      parseOSRelease(distro, sizeof(distro));

      if (uname_result == 0) {
         size_t written = xSnprintf(savedString, sizeof(savedString), "%s %s [%s]", uname_info.sysname, uname_info.release, uname_info.machine);
         if (!String_contains_i(savedString, distro, false) && sizeof(savedString) > written)
            snprintf(savedString + written, sizeof(savedString) - written, " @ %s", distro);
      } else {
         snprintf(savedString, sizeof(savedString), "%s", distro);
      }

      loaded_data = true;
   }

   return savedString;
}

function: parseOSRelease

static void parseOSRelease(char* buffer, size_t bufferLen) {
   FILE* fp = fopen(OSRELEASEFILE, "r");
   if (!fp) {
      xSnprintf(buffer, bufferLen, "No OS Release");
      return;
   }

   char name[64] = {'\0'};
   char version[64] = {'\0'};
   char lineBuffer[256];
   while (fgets(lineBuffer, sizeof(lineBuffer), fp)) {
      if (String_startsWith(lineBuffer, "PRETTY_NAME=\"")) {
         const char* start = lineBuffer + strlen("PRETTY_NAME=\"");
         const char* stop = strrchr(lineBuffer, '"');
         if (!stop || stop <= start)
            continue;
         String_safeStrncpy(buffer, start, MINIMUM(bufferLen, (size_t)(stop - start + 1)));
         fclose(fp);
         return;
      }
      if (String_startsWith(lineBuffer, "NAME=\"")) {
         const char* start = lineBuffer + strlen("NAME=\"");
         const char* stop = strrchr(lineBuffer, '"');
         if (!stop || stop <= start)
            continue;
         String_safeStrncpy(name, start, MINIMUM(sizeof(name), (size_t)(stop - start + 1)));
         continue;
      }
      if (String_startsWith(lineBuffer, "VERSION=\"")) {
         const char* start = lineBuffer + strlen("VERSION=\"");
         const char* stop = strrchr(lineBuffer, '"');
         if (!stop || stop <= start)
            continue;
         String_safeStrncpy(version, start, MINIMUM(sizeof(version), (size_t)(stop - start + 1)));
         continue;
      }
   }
   fclose(fp);

   snprintf(buffer, bufferLen, "%s%s%s", name[0] ? name : "", name[0] && version[0] ? " " : "", version);
}

uname.h

generic/uname.h

function: Generic_uname

char* Generic_uname(void);

Hashtable.c

Hashtable.c

function: Hashtable_clear

void Hashtable_clear(Hashtable* this) {
   assert(Hashtable_isConsistent(this));

   if (this->owner)
      for (size_t i = 0; i < this->size; i++)
         free(this->buckets[i].value);

   memset(this->buckets, 0, this->size * sizeof(HashtableItem));
   this->items = 0;

   assert(Hashtable_isConsistent(this));
}

function: Hashtable_count

size_t Hashtable_count(const Hashtable* this) {
   size_t items = 0;
   for (size_t i = 0; i < this->size; i++) {
      if (this->buckets[i].value)
         items++;
   }
   assert(items == this->items);
   return items;
}

function: Hashtable_delete

void Hashtable_delete(Hashtable* this) {
   Hashtable_clear(this);

   free(this->buckets);
   free(this);
}

function: Hashtable_dump

static void Hashtable_dump(const Hashtable* this) {
   fprintf(stderr, "Hashtable %p: size=%zu items=%zu owner=%s\n",
           (const void*)this,
           this->size,
           this->items,
           this->owner ? "yes" : "no");

   size_t items = 0;
   for (size_t i = 0; i < this->size; i++) {
      fprintf(stderr, "  item %5zu: key = %5u probe = %2zu value = %p\n",
              i,
              this->buckets[i].key,
              this->buckets[i].probe,
              this->buckets[i].value);

      if (this->buckets[i].value)
         items++;
   }

   fprintf(stderr, "Hashtable %p: items=%zu counted=%zu\n",
           (const void*)this,
           this->items,
           items);
}

function: Hashtable_foreach

void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
   assert(Hashtable_isConsistent(this));
   for (size_t i = 0; i < this->size; i++) {
      HashtableItem* walk = &this->buckets[i];
      if (walk->value)
         f(walk->key, walk->value, userData);
   }
   assert(Hashtable_isConsistent(this));
}

function: Hashtable_get

void* Hashtable_get(Hashtable* this, ht_key_t key) {
   size_t index = key % this->size;
   size_t probe = 0;
   void* res = NULL;
#ifndef NDEBUG
   size_t origIndex = index;
#endif

   assert(Hashtable_isConsistent(this));

   while (this->buckets[index].value) {
      if (this->buckets[index].key == key) {
         res = this->buckets[index].value;
         break;
      }

      if (this->buckets[index].probe < probe)
         break;

      index = (index + 1) != this->size ? (index + 1) : 0;
      probe++;

      assert(index != origIndex);
   }

   return res;
}

function: Hashtable_isConsistent

static bool Hashtable_isConsistent(const Hashtable* this) {
   size_t items = 0;
   for (size_t i = 0; i < this->size; i++) {
      if (this->buckets[i].value)
         items++;
   }
   bool res = items == this->items;
   if (!res)
      Hashtable_dump(this);
   return res;
}

function: Hashtable_new

Hashtable* Hashtable_new(size_t size, bool owner) {
   size = size ? nextPrime(size) : 13;

   Hashtable* this = xMalloc(sizeof(Hashtable));
   *this = (Hashtable) {
      .items = 0,
      .size = size,
      .buckets = xCalloc(size, sizeof(HashtableItem)),
      .owner = owner,
   };

   assert(Hashtable_isConsistent(this));
   return this;
}

function: Hashtable_put

void Hashtable_put(Hashtable* this, ht_key_t key, void* value) {

   assert(Hashtable_isConsistent(this));
   assert(this->size > 0);
   assert(value);

   /* grow on load-factor > 0.7 */
   if (10 * this->items > 7 * this->size) {
      if (SIZE_MAX / 2 < this->size)
         CRT_fatalError("Hashtable: size overflow");

      Hashtable_setSize(this, 2 * this->size);
   }

   insert(this, key, value);

   assert(Hashtable_isConsistent(this));
   assert(Hashtable_get(this, key) != NULL);
   assert(this->size > this->items);
}

function: Hashtable_remove

void* Hashtable_remove(Hashtable* this, ht_key_t key) {
   size_t index = key % this->size;
   size_t probe = 0;
#ifndef NDEBUG
   size_t origIndex = index;
#endif

   assert(Hashtable_isConsistent(this));

   void* res = NULL;

   while (this->buckets[index].value) {
      if (this->buckets[index].key == key) {
         if (this->owner) {
            free(this->buckets[index].value);
         } else {
            res = this->buckets[index].value;
         }

         size_t next = (index + 1) % this->size;

         while (this->buckets[next].value && this->buckets[next].probe > 0) {
            this->buckets[index] = this->buckets[next];
            this->buckets[index].probe -= 1;

            index = next;
            next = (index + 1) % this->size;
         }

         /* set empty after backward shifting */
         this->buckets[index].value = NULL;
         this->items--;

         break;
      }

      if (this->buckets[index].probe < probe)
         break;

      index = (index + 1) % this->size;
      probe++;

      assert(index != origIndex);
   }

   assert(Hashtable_isConsistent(this));
   assert(Hashtable_get(this, key) == NULL);

   /* shrink on load-factor < 0.125 */
   if (8 * this->items < this->size)
      Hashtable_setSize(this, this->size / 3); /* account for nextPrime rounding up */

   return res;
}

function: Hashtable_setSize

void Hashtable_setSize(Hashtable* this, size_t size) {

   assert(Hashtable_isConsistent(this));

   if (size <= this->items)
      return;

   size_t newSize = nextPrime(size);
   if (newSize == this->size)
      return;

   HashtableItem* oldBuckets = this->buckets;
   size_t oldSize = this->size;

   this->size = newSize;
   this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
   this->items = 0;

   /* rehash */
   for (size_t i = 0; i < oldSize; i++) {
      if (!oldBuckets[i].value)
         continue;

      insert(this, oldBuckets[i].key, oldBuckets[i].value);
   }

   free(oldBuckets);

   assert(Hashtable_isConsistent(this));
}

function: insert

static void insert(Hashtable* this, ht_key_t key, void* value) {
   size_t index = key % this->size;
   size_t probe = 0;
#ifndef NDEBUG
   size_t origIndex = index;
#endif

   for (;;) {
      if (!this->buckets[index].value) {
         this->items++;
         this->buckets[index].key = key;
         this->buckets[index].probe = probe;
         this->buckets[index].value = value;
         return;
      }

      if (this->buckets[index].key == key) {
         if (this->owner && this->buckets[index].value != value)
            free(this->buckets[index].value);
         this->buckets[index].value = value;
         return;
      }

      /* Robin Hood swap */
      if (probe > this->buckets[index].probe) {
         HashtableItem tmp = this->buckets[index];

         this->buckets[index].key = key;
         this->buckets[index].probe = probe;
         this->buckets[index].value = value;

         key = tmp.key;
         probe = tmp.probe;
         value = tmp.value;
      }

      index = (index + 1) % this->size;
      probe++;

      assert(index != origIndex);
   }
}

function: nextPrime

static size_t nextPrime(size_t n) {
   /* on 32-bit make sure we do not return primes not fitting in size_t */
   for (size_t i = 0; i < ARRAYSIZE(OEISprimes) && OEISprimes[i] < SIZE_MAX; i++) {
      if (n <= OEISprimes[i])
         return OEISprimes[i];
   }

   CRT_fatalError("Hashtable: no prime found");
}

struct: Hashtable

struct Hashtable_ {
   size_t size;
   HashtableItem* buckets;
   size_t items;
   bool owner;
};

struct: HashtableItem

typedef struct HashtableItem_ {
   ht_key_t key;
   size_t probe;
   void* value;
} HashtableItem;

Hashtable.h

Hashtable.h

function: Hashtable_clear

void Hashtable_clear(Hashtable* this);

function: Hashtable_count

size_t Hashtable_count(const Hashtable* this);

function: Hashtable_delete

void Hashtable_delete(Hashtable* this);

function: Hashtable_foreach

void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);

function: Hashtable_get

void* Hashtable_get(Hashtable* this, ht_key_t key);

function: Hashtable_new

Hashtable* Hashtable_new(size_t size, bool owner);

function: Hashtable_put

void Hashtable_put(Hashtable* this, ht_key_t key, void* value);

function: Hashtable_remove

void* Hashtable_remove(Hashtable* this, ht_key_t key);

function: Hashtable_setSize

void Hashtable_setSize(Hashtable* this, size_t size);

struct: Hashtable

typedef struct Hashtable_ Hashtable;

typedef: Hashtable_PairFunction

typedef void(*Hashtable_PairFunction)(ht_key_t key, void* value, void* userdata);

typedef: ht_key_t

typedef unsigned int ht_key_t;

Header.c

Header.c

function: calcColumnWidthCount

static int calcColumnWidthCount(const Header* this, const Meter* curMeter, const int pad, const unsigned int curColumn, const int curHeight) {
   for (size_t i = curColumn + 1; i < HeaderLayout_getColumns(this->headerLayout); i++) {
      const Vector* meters = this->columns[i];

      int height = pad;
      for (int j = 0; j < Vector_size(meters); j++) {
         const Meter* meter = (const Meter*) Vector_get(meters, j);

         if (height >= curHeight + curMeter->h)
            break;

         height += meter->h;
         if (height <= curHeight)
            continue;

         if (!Object_isA((const Object*) meter, (const ObjectClass*) &BlankMeter_class))
            return i - curColumn;
      }
   }

   return HeaderLayout_getColumns(this->headerLayout) - curColumn;
}

function: Header_addMeterByClass

Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column) {
   assert(column < HeaderLayout_getColumns(this->headerLayout));

   Vector* meters = this->columns[column];

   Meter* meter = Meter_new(this->host, param, type);
   Vector_add(meters, meter);
   return meter;
}

function: Header_addMeterByName

static void Header_addMeterByName(Header* this, const char* name, MeterModeId mode, unsigned int column) {
   assert(column < HeaderLayout_getColumns(this->headerLayout));

   Vector* meters = this->columns[column];

   const char* paren = strchr(name, '(');
   unsigned int param = 0;
   size_t nameLen;
   if (paren) {
      if (sscanf(paren, "(%10u)", &param) != 1) { // not CPUMeter
         char dynamic[32] = {0};
         if (sscanf(paren, "(%30s)", dynamic) == 1) { // DynamicMeter
            char* end;
            if ((end = strrchr(dynamic, ')')) == NULL)
               return;    // htoprc parse failure
            *end = '\0';
            const Settings* settings = this->host->settings;
            if (!DynamicMeter_search(settings->dynamicMeters, dynamic, &param))
               return;    // name lookup failure
         } else {
            param = 0;
         }
      }
      nameLen = paren - name;
   } else {
      nameLen = strlen(name);
   }

   for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
      if (0 == strncmp(name, (*type)->name, nameLen) && (*type)->name[nameLen] == '\0') {
         Meter* meter = Meter_new(this->host, param, *type);
         if (mode != 0) {
            Meter_setMode(meter, mode);
         }
         Vector_add(meters, meter);
         break;
      }
   }
}

function: Header_calculateHeight

int Header_calculateHeight(Header* this) {
   const Settings* settings = this->host->settings;
   const int pad = settings->headerMargin ? 2 : 0;
   int maxHeight = pad;

   Header_forEachColumn(this, col) {
      const Vector* meters = this->columns[col];
      int height = pad;
      for (int i = 0; i < Vector_size(meters); i++) {
         Meter* meter = (Meter*) Vector_get(meters, i);
         meter->columnWidthCount = calcColumnWidthCount(this, meter, pad, col, height);
         height += meter->h;
      }
      maxHeight = MAXIMUM(maxHeight, height);
   }

   if (maxHeight == pad) {
      maxHeight = 0;
      this->pad = 0;
   } else {
      this->pad = pad;
   }

   if (settings->screenTabs) {
      maxHeight++;
   }

   this->height = maxHeight;

   return maxHeight;
}

function: Header_delete

void Header_delete(Header* this) {
   Header_forEachColumn(this, i) {
      Vector_delete(this->columns[i]);
   }

   free(this->columns);
   free(this);
}

function: Header_draw

void Header_draw(const Header* this) {
   const int height = this->height;
   const int pad = this->pad;
   attrset(CRT_colors[RESET_COLOR]);
   for (int y = 0; y < height; y++) {
      mvhline(y, 0, ' ', COLS);
   }
   const int numCols = HeaderLayout_getColumns(this->headerLayout);
   const int width = COLS - 2 * pad - (numCols - 1);
   int x = pad;
   float roundingLoss = 0.0F;

   Header_forEachColumn(this, col) {
      Vector* meters = this->columns[col];
      float colWidth = (float)width * HeaderLayout_layouts[this->headerLayout].widths[col] / 100.0F;

      roundingLoss += colWidth - floorf(colWidth);
      if (roundingLoss >= 1.0F) {
         colWidth += 1.0F;
         roundingLoss -= 1.0F;
      }

      for (int y = (pad / 2), i = 0; i < Vector_size(meters); i++) {
         Meter* meter = (Meter*) Vector_get(meters, i);

         float actualWidth = colWidth;

         /* Let meters in text mode expand to the right on empty neighbors;
            except for multi column meters. */
         if (meter->mode == TEXT_METERMODE && !Meter_isMultiColumn(meter)) {
            for (int j = 1; j < meter->columnWidthCount; j++) {
               actualWidth++; /* separator column */
               actualWidth += (float)width * HeaderLayout_layouts[this->headerLayout].widths[col + j] / 100.0F;
            }
         }

         assert(meter->draw);
         meter->draw(meter, x, y, floorf(actualWidth));
         y += meter->h;
      }

      x += floorf(colWidth);
      x++; /* separator column */
   }
}

function: Header_new

Header* Header_new(Machine* host, HeaderLayout hLayout) {
   Header* this = xCalloc(1, sizeof(Header));
   this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*));
   this->headerLayout = hLayout;
   this->host = host;

   Header_forEachColumn(this, i) {
      this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
   }

   return this;
}

function: Header_populateFromSettings

void Header_populateFromSettings(Header* this) {
   const Settings* settings = this->host->settings;
   Header_setLayout(this, settings->hLayout);

   Header_forEachColumn(this, col) {
      const MeterColumnSetting* colSettings = &settings->hColumns[col];
      Vector_prune(this->columns[col]);
      for (size_t i = 0; i < colSettings->len; i++) {
         Header_addMeterByName(this, colSettings->names[i], colSettings->modes[i], col);
      }
   }

   Header_calculateHeight(this);
}

function: Header_reinit

void Header_reinit(Header* this) {
   Header_forEachColumn(this, col) {
      for (int i = 0; i < Vector_size(this->columns[col]); i++) {
         Meter* meter = (Meter*) Vector_get(this->columns[col], i);
         if (Meter_initFn(meter)) {
            Meter_init(meter);
         }
      }
   }
}

function: Header_setLayout

void Header_setLayout(Header* this, HeaderLayout hLayout) {
   size_t oldColumns = HeaderLayout_getColumns(this->headerLayout);
   size_t newColumns = HeaderLayout_getColumns(hLayout);

   this->headerLayout = hLayout;

   if (newColumns == oldColumns)
      return;

   if (newColumns > oldColumns) {
      this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
      for (size_t i = oldColumns; i < newColumns; i++)
         this->columns[i] = Vector_new(Class(Meter), true, DEFAULT_SIZE);
   } else {
      // move meters from to-be-deleted columns into last one
      for (size_t i = newColumns; i < oldColumns; i++) {
         for (int j = this->columns[i]->items - 1; j >= 0; j--) {
            Vector_add(this->columns[newColumns - 1], Vector_take(this->columns[i], j));
         }
         Vector_delete(this->columns[i]);
      }
      this->columns = xReallocArray(this->columns, newColumns, sizeof(Vector*));
   }

   Header_calculateHeight(this);
}

function: Header_updateData

void Header_updateData(Header* this) {
   Header_forEachColumn(this, col) {
      Vector* meters = this->columns[col];
      int items = Vector_size(meters);
      for (int i = 0; i < items; i++) {
         Meter* meter = (Meter*) Vector_get(meters, i);
         Meter_updateValues(meter);
      }
   }
}

function: Header_writeBackToSettings

void Header_writeBackToSettings(const Header* this) {
   Settings* settings = this->host->settings;
   Settings_setHeaderLayout(settings, this->headerLayout);

   Header_forEachColumn(this, col) {
      MeterColumnSetting* colSettings = &settings->hColumns[col];

      if (colSettings->names) {
         for (size_t j = 0; j < colSettings->len; j++)
            free(colSettings->names[j]);
         free(colSettings->names);
      }
      free(colSettings->modes);

      const Vector* vec = this->columns[col];
      int len = Vector_size(vec);

      colSettings->names = len ? xCalloc(len + 1, sizeof(*colSettings->names)) : NULL;
      colSettings->modes = len ? xCalloc(len, sizeof(*colSettings->modes)) : NULL;
      colSettings->len = len;

      for (int i = 0; i < len; i++) {
         const Meter* meter = (Meter*) Vector_get(vec, i);
         char* name;
         if (meter->param && As_Meter(meter) == &DynamicMeter_class) {
            const char* dynamic = DynamicMeter_lookup(settings->dynamicMeters, meter->param);
            xAsprintf(&name, "%s(%s)", As_Meter(meter)->name, dynamic);
         } else if (meter->param && As_Meter(meter) == &CPUMeter_class) {
            xAsprintf(&name, "%s(%u)", As_Meter(meter)->name, meter->param);
         } else {
            xAsprintf(&name, "%s", As_Meter(meter)->name);
         }
         colSettings->names[i] = name;
         colSettings->modes[i] = meter->mode;
      }
   }
}

Header.h

Header.h

function: Header_addMeterByClass

Meter* Header_addMeterByClass(Header* this, const MeterClass* type, unsigned int param, unsigned int column);

function: Header_calculateHeight

int Header_calculateHeight(Header* this);

function: Header_delete

void Header_delete(Header* this);

function: Header_draw

void Header_draw(const Header* this);

function: Header_new

Header* Header_new(Machine* host, HeaderLayout hLayout);

function: Header_populateFromSettings

void Header_populateFromSettings(Header* this);

function: Header_reinit

void Header_reinit(Header* this);

function: Header_setLayout

void Header_setLayout(Header* this, HeaderLayout hLayout);

function: Header_updateData

void Header_updateData(Header* this);

function: Header_writeBackToSettings

void Header_writeBackToSettings(const Header* this);

macro: Header_forEachColumn

#define Header_forEachColumn(this_, i_) for (size_t (i_)=0, H_fEC_numColumns_ = HeaderLayout_getColumns((this_)->headerLayout); (i_) < H_fEC_numColumns_; ++(i_))

struct: Header

typedef struct Header_ {
   Vector** columns;
   Machine* host;
   HeaderLayout headerLayout;
   int pad;
   int height;
} Header;

HeaderLayout.h

HeaderLayout.h

enum: HeaderLayout

typedef enum HeaderLayout_ {
   HF_INVALID = -1,
   HF_ONE_100,
   HF_TWO_50_50,
   HF_TWO_33_67,
   HF_TWO_67_33,
   HF_THREE_33_34_33,
   HF_THREE_25_25_50,
   HF_THREE_25_50_25,
   HF_THREE_50_25_25,
   HF_THREE_40_30_30,
   HF_THREE_30_40_30,
   HF_THREE_30_30_40,
   HF_THREE_40_20_40,
   HF_FOUR_25_25_25_25,
   LAST_HEADER_LAYOUT
} HeaderLayout;

function: HeaderLayout_fromName

static inline HeaderLayout HeaderLayout_fromName(const char* name) {
   for (size_t i = 0; i < LAST_HEADER_LAYOUT; i++) {
      if (String_eq(HeaderLayout_layouts[i].name, name))
         return (HeaderLayout) i;
   }

   return LAST_HEADER_LAYOUT;
}

function: HeaderLayout_getColumns

static inline size_t HeaderLayout_getColumns(HeaderLayout hLayout) {
   /* assert the layout is initialized */
   assert(0 <= hLayout);
   assert(hLayout < LAST_HEADER_LAYOUT);
   assert(HeaderLayout_layouts[hLayout].name[0]);
   assert(HeaderLayout_layouts[hLayout].description[0]);
   return HeaderLayout_layouts[hLayout].columns;
}

function: HeaderLayout_getName

static inline const char* HeaderLayout_getName(HeaderLayout hLayout) {
   /* assert the layout is initialized */
   assert(0 <= hLayout);
   assert(hLayout < LAST_HEADER_LAYOUT);
   assert(HeaderLayout_layouts[hLayout].name[0]);
   assert(HeaderLayout_layouts[hLayout].description[0]);
   return HeaderLayout_layouts[hLayout].name;
}

global variable: HeaderLayout_layouts

static const struct {
   uint8_t columns;
   const uint8_t widths[4];
   const char* name;
   const char* description;
} HeaderLayout_layouts[LAST_HEADER_LAYOUT] = {
   [HF_ONE_100]          = { 1, { 100, 0,  0,  0 }, "one_100",          "1 column  - full width",      },
   [HF_TWO_50_50]        = { 2, { 50, 50,  0,  0 }, "two_50_50",        "2 columns - 50/50 (default)", },
   [HF_TWO_33_67]        = { 2, { 33, 67,  0,  0 }, "two_33_67",        "2 columns - 33/67",           },
   [HF_TWO_67_33]        = { 2, { 67, 33,  0,  0 }, "two_67_33",        "2 columns - 67/33",           },
   [HF_THREE_33_34_33]   = { 3, { 33, 34, 33,  0 }, "three_33_34_33",   "3 columns - 33/34/33",        },
   [HF_THREE_25_25_50]   = { 3, { 25, 25, 50,  0 }, "three_25_25_50",   "3 columns - 25/25/50",        },
   [HF_THREE_25_50_25]   = { 3, { 25, 50, 25,  0 }, "three_25_50_25",   "3 columns - 25/50/25",        },
   [HF_THREE_50_25_25]   = { 3, { 50, 25, 25,  0 }, "three_50_25_25",   "3 columns - 50/25/25",        },
   [HF_THREE_40_30_30]   = { 3, { 40, 30, 30,  0 }, "three_40_30_30",   "3 columns - 40/30/30",        },
   [HF_THREE_30_40_30]   = { 3, { 30, 40, 30,  0 }, "three_30_40_30",   "3 columns - 30/40/30",        },
   [HF_THREE_30_30_40]   = { 3, { 30, 30, 40,  0 }, "three_30_30_40",   "3 columns - 30/30/40",        },
   [HF_THREE_40_20_40]   = { 3, { 40, 20, 40,  0 }, "three_40_20_40",   "3 columns - 40/20/40",        },
   [HF_FOUR_25_25_25_25] = { 4, { 25, 25, 25, 25 }, "four_25_25_25_25", "4 columns - 25/25/25/25",     },
};

HeaderOptionsPanel.c

HeaderOptionsPanel.c

class definition: HeaderOptionsPanel_class

const PanelClass HeaderOptionsPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = HeaderOptionsPanel_delete
   },
   .eventHandler = HeaderOptionsPanel_eventHandler
};

function: HeaderOptionsPanel_delete

static void HeaderOptionsPanel_delete(Object* object) {
   HeaderOptionsPanel* this = (HeaderOptionsPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: HeaderOptionsPanel_eventHandler

static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
   HeaderOptionsPanel* this = (HeaderOptionsPanel*) super;

   HandlerResult result = IGNORED;

   switch (ch) {
      case 0x0a:
      case 0x0d:
      case KEY_ENTER:
      case KEY_MOUSE:
      case KEY_RECLICK:
      case ' ': {
         int mark = Panel_getSelectedIndex(super);
         assert(mark >= 0);
         assert(mark < LAST_HEADER_LAYOUT);

         for (int i = 0; i < LAST_HEADER_LAYOUT; i++)
            CheckItem_set((CheckItem*)Panel_get(super, i), false);
         CheckItem_set((CheckItem*)Panel_get(super, mark), true);

         Header_setLayout(this->scr->header, mark);
         this->settings->changed = true;
         this->settings->lastUpdate++;

         ScreenManager_resize(this->scr);

         result = HANDLED;
      }
   }

   return result;
}

function: HeaderOptionsPanel_new

HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr) {
   HeaderOptionsPanel* this = AllocThis(HeaderOptionsPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(HeaderOptionsFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);

   this->scr = scr;
   this->settings = settings;

   Panel_setHeader(super, "Header Layout");
   for (int i = 0; i < LAST_HEADER_LAYOUT; i++) {
      Panel_add(super, (Object*) CheckItem_newByVal(HeaderLayout_layouts[i].description, false));
   }
   CheckItem_set((CheckItem*)Panel_get(super, scr->header->headerLayout), true);
   return this;
}

static variable: HeaderOptionsFunctions

static const char* const HeaderOptionsFunctions[] = {"      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "      ", "Done  ", NULL};

HeaderOptionsPanel.h

HeaderOptionsPanel.h

extern const: HeaderOptionsPanel_class

extern const PanelClass HeaderOptionsPanel_class;

function: HeaderOptionsPanel_new

HeaderOptionsPanel* HeaderOptionsPanel_new(Settings* settings, ScreenManager* scr);

struct: HeaderOptionsPanel_

typedef struct HeaderOptionsPanel_ {
   Panel super;

   ScreenManager* scr;
   Settings* settings;
} HeaderOptionsPanel;

HostnameMeter.c

HostnameMeter.c

function: HostnameMeter_updateValues

static void HostnameMeter_updateValues(Meter* this) {
   Platform_getHostname(this->txtBuffer, sizeof(this->txtBuffer));
}

struct: HostnameMeter_class

const MeterClass HostnameMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = HostnameMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = HostnameMeter_attributes,
   .name = "Hostname",
   .uiName = "Hostname",
   .caption = "Hostname: ",
};

structure: HostnameMeter_attributes

static const int HostnameMeter_attributes[] = {
   HOSTNAME
};

HostnameMeter.h

HostnameMeter.h

struct: HostnameMeter_class

extern const MeterClass HostnameMeter_class;

htop.1.in

htop.1.in

file: htop.1.in

htop.c

htop.c

function: main

int main(int argc, char** argv) {
   return CommandLine_run(argc, argv);
}

global variable: program

const char* program = PACKAGE;

htop.desktop

htop.desktop

file: htop.desktop

htop.svg

htop.svg

file: htop.svg

IncSet.c

IncSet.c

function: IncMode_done

static inline void IncMode_done(IncMode* mode) {
   FunctionBar_delete(mode->bar);
}

function: IncMode_find

static bool IncMode_find(const IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue, int step) {
   int size = Panel_size(panel);
   int here = Panel_getSelectedIndex(panel);
   int i = here;
   for (;;) {
      i += step;
      if (i == size) {
         i = 0;
      }
      if (i == -1) {
         i = size - 1;
      }
      if (i == here) {
         return false;
      }

      if (String_contains_i(getPanelValue(panel, i), mode->buffer, true)) {
         Panel_setSelected(panel, i);
         return true;
      }
   }
}

function: IncMode_initFilter

static inline void IncMode_initFilter(IncMode* filter) {
   memset(filter, 0, sizeof(IncMode));
   filter->bar = FunctionBar_new(filterFunctions, filterKeys, filterEvents);
   filter->isFilter = true;
}

function: IncMode_initSearch

static inline void IncMode_initSearch(IncMode* search) {
   memset(search, 0, sizeof(IncMode));
   search->bar = FunctionBar_new(searchFunctions, searchKeys, searchEvents);
   search->isFilter = false;
}

function: IncMode_reset

static void IncMode_reset(IncMode* mode) {
   mode->index = 0;
   mode->buffer[0] = 0;
}

function: IncSet_activate

void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
   this->active = &(this->modes[type]);
   panel->currentBar = this->active->bar;
   panel->cursorOn = true;
   this->panel = panel;
   IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}

function: IncSet_deactivate

static void IncSet_deactivate(IncSet* this, Panel* panel) {
   this->active = NULL;
   Panel_setDefaultBar(panel);
   panel->cursorOn = false;
   FunctionBar_draw(this->defaultBar);
}

function: IncSet_delete

void IncSet_delete(IncSet* this) {
   IncMode_done(&(this->modes[0]));
   IncMode_done(&(this->modes[1]));
   free(this);
}

function: IncSet_drawBar

void IncSet_drawBar(const IncSet* this, int attr) {
   if (this->active) {
      if (!this->active->isFilter && !this->found)
         attr = CRT_colors[FAILED_SEARCH];
      int cursorX = FunctionBar_drawExtra(this->active->bar, this->active->buffer, attr, true);
      this->panel->cursorY = LINES - 1;
      this->panel->cursorX = cursorX;
   } else {
      FunctionBar_draw(this->defaultBar);
   }
}

function: IncSet_getListItemValue

const char* IncSet_getListItemValue(Panel* panel, int i) {
   const ListItem* l = (const ListItem*) Panel_get(panel, i);
   return l ? l->value : "";
}

function: IncSet_handleKey

bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
   if (ch == ERR)
      return true;

   IncMode* mode = this->active;
   int size = Panel_size(panel);
   bool filterChanged = false;
   bool doSearch = true;
   if (ch == KEY_F(3) || ch == KEY_F(15)) {
      if (size == 0)
         return true;

      IncMode_find(mode, panel, getPanelValue, ch == KEY_F(3) ? 1 : -1);
      doSearch = false;
   } else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
      if (mode->index < INCMODE_MAX) {
         mode->buffer[mode->index] = (char) ch;
         mode->index++;
         mode->buffer[mode->index] = 0;
         if (mode->isFilter) {
            filterChanged = true;
            if (mode->index == 1) {
               this->filtering = true;
            }
         }
      }
   } else if (ch == KEY_BACKSPACE || ch == 127) {
      if (mode->index > 0) {
         mode->index--;
         mode->buffer[mode->index] = 0;
         if (mode->isFilter) {
            filterChanged = true;
            if (mode->index == 0) {
               this->filtering = false;
               IncMode_reset(mode);
            }
         }
      } else {
         doSearch = false;
      }
   } else if (ch == KEY_RESIZE) {
      doSearch = (mode->index > 0);
   } else {
      if (mode->isFilter) {
         filterChanged = true;
         if (ch == 27) {
            this->filtering = false;
            IncMode_reset(mode);
         }
      } else {
         if (ch == 27) {
            IncMode_reset(mode);
         }
      }
      IncSet_deactivate(this, panel);
      doSearch = false;
   }
   if (doSearch) {
      this->found = search(this, panel, getPanelValue);
   }
   if (filterChanged && lines) {
      updateWeakPanel(this, panel, lines);
   }
   return filterChanged;
}

function: IncSet_new

IncSet* IncSet_new(FunctionBar* bar) {
   IncSet* this = xMalloc(sizeof(IncSet));
   IncMode_initSearch(&(this->modes[INC_SEARCH]));
   IncMode_initFilter(&(this->modes[INC_FILTER]));
   this->active = NULL;
   this->defaultBar = bar;
   this->filtering = false;
   this->found = false;
   return this;
}

function: IncSet_reset

void IncSet_reset(IncSet* this, IncType type) {
   IncMode_reset(&this->modes[type]);
}

function: IncSet_setFilter

void IncSet_setFilter(IncSet* this, const char* filter) {
   IncMode* mode = &this->modes[INC_FILTER];
   size_t len = String_safeStrncpy(mode->buffer, filter, sizeof(mode->buffer));
   mode->index = len;
   this->filtering = true;
}

function: IncSet_synthesizeEvent

int IncSet_synthesizeEvent(IncSet* this, int x) {
   if (this->active) {
      return FunctionBar_synthesizeEvent(this->active->bar, x);
   } else {
      return FunctionBar_synthesizeEvent(this->defaultBar, x);
   }
}

function: search

static bool search(const IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) {
   int size = Panel_size(panel);
   for (int i = 0; i < size; i++) {
      if (String_contains_i(getPanelValue(panel, i), this->active->buffer, true)) {
         Panel_setSelected(panel, i);
         return true;
      }
   }

   return false;
}

function: updateWeakPanel

static void updateWeakPanel(const IncSet* this, Panel* panel, Vector* lines) {
   const Object* selected = Panel_getSelected(panel);
   Panel_prune(panel);
   if (this->filtering) {
      int n = 0;
      const char* incFilter = this->modes[INC_FILTER].buffer;
      for (int i = 0; i < Vector_size(lines); i++) {
         ListItem* line = (ListItem*)Vector_get(lines, i);
         if (String_contains_i(line->value, incFilter, true)) {
            Panel_add(panel, (Object*)line);
            if (selected == (Object*)line) {
               Panel_setSelected(panel, n);
            }

            n++;
         }
      }
   } else {
      for (int i = 0; i < Vector_size(lines); i++) {
         Object* line = Vector_get(lines, i);
         Panel_add(panel, line);
         if (selected == line) {
            Panel_setSelected(panel, i);
         }
      }
   }
}

IncSet.h

IncSet.h

enum: IncType

typedef enum {
   INC_SEARCH = 0,
   INC_FILTER = 1
} IncType;

function: IncSet_activate

void IncSet_activate(IncSet* this, IncType type, Panel* panel);

function: IncSet_delete

void IncSet_delete(IncSet* this);

function: IncSet_drawBar

void IncSet_drawBar(const IncSet* this, int attr);

function: IncSet_filter

static inline const char* IncSet_filter(const IncSet* this) {
   return this->filtering ? this->modes[INC_FILTER].buffer : NULL;
}

function: IncSet_getListItemValue

const char* IncSet_getListItemValue(Panel* panel, int i);

function: IncSet_handleKey

bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);

function: IncSet_new

IncSet* IncSet_new(FunctionBar* bar);

function: IncSet_reset

void IncSet_reset(IncSet* this, IncType type);

function: IncSet_setFilter

void IncSet_setFilter(IncSet* this, const char* filter);

function: IncSet_synthesizeEvent

int IncSet_synthesizeEvent(IncSet* this, int x);

macro: INCMODE_MAX

#define INCMODE_MAX 128

struct: IncMode

typedef struct IncMode_ {
   char buffer[INCMODE_MAX + 1];
   int index;
   FunctionBar* bar;
   bool isFilter;
} IncMode;

struct: IncSet

typedef struct IncSet_ {
   IncMode modes[2];
   IncMode* active;
   Panel* panel;
   FunctionBar* defaultBar;
   bool filtering;
   bool found;
} IncSet;

typedef: IncMode_GetPanelValue

typedef const char* (*IncMode_GetPanelValue)(Panel*, int);

InfoScreen.c

InfoScreen.c

function: InfoScreen_addLine

void InfoScreen_addLine(InfoScreen* this, const char* line) {
   Vector_add(this->lines, (Object*) ListItem_new(line, 0));
   const char* incFilter = IncSet_filter(this->inc);
   if (!incFilter || String_contains_i(line, incFilter, true)) {
      Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1));
   }
}

function: InfoScreen_appendLine

void InfoScreen_appendLine(InfoScreen* this, const char* line) {
   ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1);
   ListItem_append(last, line);
   const char* incFilter = IncSet_filter(this->inc);
   if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter, true)) {
      Panel_add(this->display, (Object*)last);
   }
}

function: InfoScreen_done

InfoScreen* InfoScreen_done(InfoScreen* this) {
   Panel_delete((Object*)this->display);
   IncSet_delete(this->inc);
   Vector_delete(this->lines);
   return this;
}

function: InfoScreen_drawTitled

void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);

   char title[COLS + 1];
   int len = vsnprintf(title, sizeof(title), fmt, ap);
   va_end(ap);

   if (len > COLS) {
      memset(&title[COLS - 3], '.', 3);
   }

   attrset(CRT_colors[METER_TEXT]);
   mvhline(0, 0, ' ', COLS);
   mvaddstr(0, 0, title);
   attrset(CRT_colors[DEFAULT_COLOR]);
   Panel_draw(this->display, true, true, true, false);

   IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}

function: InfoScreen_init

InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
   this->process = process;
   if (!bar) {
      bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
   }
   this->display = Panel_new(0, 1, COLS, height, Class(ListItem), false, bar);
   this->inc = IncSet_new(bar);
   this->lines = Vector_new(Vector_type(this->display->items), true, DEFAULT_SIZE);
   Panel_setHeader(this->display, panelHeader);
   return this;
}

function: InfoScreen_run

global variable: InfoScreenEvents

static const int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};

global variable: InfoScreenFunctions

static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done   ", NULL};

global variable: InfoScreenKeys

static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};

InfoScreen.h

InfoScreen.h

function: InfoScreen_addLine

void InfoScreen_addLine(InfoScreen* this, const char* line);

function: InfoScreen_appendLine

void InfoScreen_appendLine(InfoScreen* this, const char* line);

function: InfoScreen_done

InfoScreen* InfoScreen_done(InfoScreen* this);

function: InfoScreen_drawTitled

ATTR_FORMAT(printf, 2, 3)
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);

function: InfoScreen_init

InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader);

function: InfoScreen_run

void InfoScreen_run(InfoScreen* this);

macro: As_InfoScreen

#define As_InfoScreen(this_)          ((const InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))

macro: InfoScreen_draw

#define InfoScreen_draw(this_)        As_InfoScreen(this_)->draw((InfoScreen*)(this_))

macro: InfoScreen_onErr

#define InfoScreen_onErr(this_)       As_InfoScreen(this_)->onErr((InfoScreen*)(this_))

macro: InfoScreen_onKey

#define InfoScreen_onKey(this_, ch_)  As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)

macro: InfoScreen_scan

#define InfoScreen_scan(this_)        As_InfoScreen(this_)->scan((InfoScreen*)(this_))

struct: InfoScreen

typedef struct InfoScreen_ {
   Object super;
   const Process* process;
   Panel* display;
   IncSet* inc;
   Vector* lines;
} InfoScreen;

struct: InfoScreenClass

typedef struct InfoScreenClass_ {
   const ObjectClass super;
   const InfoScreen_Scan scan;
   const InfoScreen_Draw draw;
   const InfoScreen_OnErr onErr;
   const InfoScreen_OnKey onKey;
} InfoScreenClass;

typedef: InfoScreen_Draw

typedef void(*InfoScreen_Draw)(InfoScreen*);

typedef: InfoScreen_OnErr

typedef void(*InfoScreen_OnErr)(InfoScreen*);

typedef: InfoScreen_OnKey

typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);

typedef: InfoScreen_Scan

typedef void(*InfoScreen_Scan)(InfoScreen*);

htop.imp

iwyu/htop.imp

file: htop.imp

[
    { include: ["<curses.h>", "private", "\"ProvideCurses.h\"", "public"] },
    { include: ["<ncurses.h>", "private", "\"ProvideCurses.h\"", "public"] },
    { include: ["<ncurses/curses.h>", "private", "\"ProvideCurses.h\"", "public"] },
    { include: ["<ncurses/ncurses.h>", "private", "\"ProvideCurses.h\"", "public"] },

    { include: ["<term.h>", "private", "\"ProvideTerm.h\"", "public"] },
    { include: ["<ncurses/term.h>", "private", "\"ProvideTerm.h\"", "public"] },
    { include: ["<ncursesw/term.h>", "private", "\"ProvideTerm.h\"", "public"] },

    { include: ["<libunwind-x86_64.h>", "private", "<libunwind.h>", "public"] },
    { include: ["\"ibunwind-x86_64.h\"", "private", "<libunwind.h>", "public"] },

    { include: ["<bits/types/struct_tm.h>", "private", "<time.h>", "public"] },

    { include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] },

    { include: ["<sys/dirent.h>", "private", "<dirent.h>", "public"] },

    { include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },

    { include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] },

    { include: ["<sys/limits.h>", "private", "<limits.h>", "public"] },

    { include: ["<x86/_inttypes.h>", "private", "<inttypes.h>", "public"] },

    { include: ["<linux/capability.h>", "private", "<sys/capability.h>", "public"] },

    { include: ["<bits/mman-shared.h>", "private", "<sys/mman.h>", "public"] },

    { include: ["<bits/types/struct_sched_param.h>", "private", "<sched.h>", "public"] },
]

run_iwyu.sh

iwyu/run_iwyu.sh

script: run_iwyu.sh

#!/bin/sh

SCRIPT=$(readlink -f "$0")
SCRIPTDIR=$(dirname "$SCRIPT")
SOURCEDIR="$SCRIPTDIR/.."

PKG_NL3=$(pkg-config --cflags libnl-3.0)

IWYU=${IWYU:-iwyu}

cd "$SOURCEDIR" || exit

./configure CC=clang CXX=clang++ --enable-silent-rules
make clean
make -k -s CC="$IWYU" CFLAGS="-Xiwyu --no_comments -Xiwyu --no_fwd_decl -Xiwyu --mapping_file='$SCRIPTDIR/htop.imp' $PKG_NL3"

CGroupUtils.c

linux/CGroupUtils.c

function: CGroup_filterContainer

char* CGroup_filterContainer(const char* cgroup) {
   StrBuf_state s = {
      .buf = NULL,
      .size = 0,
      .pos = 0,
   };

   if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_count)) {
      return NULL;
   }

   if (!s.pos) {
      return xStrdup("/");
   }

   s.buf = xCalloc(s.pos + 1, sizeof(char));
   s.size = s.pos;
   s.pos = 0;

   if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_write)) {
      free(s.buf);
      return NULL;
   }

   s.buf[s.size] = '\0';
   return s.buf;
}

function: CGroup_filterContainer_internal

function: CGroup_filterName

char* CGroup_filterName(const char* cgroup) {
   StrBuf_state s = {
      .buf = NULL,
      .size = 0,
      .pos = 0,
   };

   if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_count)) {
      return NULL;
   }

   s.buf = xCalloc(s.pos + 1, sizeof(char));
   s.size = s.pos;
   s.pos = 0;

   if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_write)) {
      free(s.buf);
      return NULL;
   }

   s.buf[s.size] = '\0';
   return s.buf;
}

function: CGroup_filterName_internal

function: Label_checkEqual

static bool Label_checkEqual(const char* labelStart, size_t labelLen, const char* expected) {
   return labelLen == strlen(expected) && String_startsWith(labelStart, expected);
}

function: Label_checkPrefix

static bool Label_checkPrefix(const char* labelStart, size_t labelLen, const char* expected) {
   return labelLen > strlen(expected) && String_startsWith(labelStart, expected);
}

function: Label_checkSuffix

static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const char* expected) {
   return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected);
}

function: StrBuf_putc_count

static bool StrBuf_putc_count(StrBuf_state* p, ATTR_UNUSED char c) {
   p->pos++;
   return true;
}

function: StrBuf_putc_write

static bool StrBuf_putc_write(StrBuf_state* p, char c) {
   if (p->pos >= p->size)
      return false;

   p->buf[p->pos] = c;
   p->pos++;
   return true;
}

function: StrBuf_putsn

static bool StrBuf_putsn(StrBuf_state* p, StrBuf_putc_t w, const char* s, size_t count) {
   for (; count; count--)
      if (!w(p, *s++))
         return false;

   return true;
}

function: StrBuf_putsz

static bool StrBuf_putsz(StrBuf_state* p, StrBuf_putc_t w, const char* s) {
   while (*s)
      if (!w(p, *s++))
         return false;

   return true;
}

struct: StrBuf_state

typedef struct StrBuf_state {
   char* buf;
   size_t size;
   size_t pos;
} StrBuf_state;

typedef: StrBuf_putc_t

typedef bool (*StrBuf_putc_t)(StrBuf_state* p, char c);

CGroupUtils.h

linux/CGroupUtils.h

function: CGroup_filterContainer

char* CGroup_filterContainer(const char* cgroup);

function: CGroup_filterName

char* CGroup_filterName(const char* cgroup);

GPU.c

linux/GPU.c

file: GPU.c

GPU.h

linux/GPU.h

function: GPU_readProcessData

void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t procFd);

GPUMeter.c

linux/GPUMeter.c

file: GPUMeter.c

GPUMeter.h

linux/GPUMeter.h

function: GPUMeter_active

bool GPUMeter_active(void);

variable: GPUMeter_class

extern const MeterClass GPUMeter_class;

HugePageMeter.c

linux/HugePageMeter.c

function: HugePageMeter_display

static void HugePageMeter_display(const Object* cast, RichString* out) {
   char buffer[50];
   const Meter* this = (const Meter*)cast;

   RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
   Meter_humanUnit(buffer, this->total, sizeof(buffer));
   RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);

   for (unsigned i = 0; i < ARRAYSIZE(HugePageMeter_active_labels); i++) {
      if (!HugePageMeter_active_labels[i]) {
         break;
      }
      RichString_appendAscii(out, CRT_colors[METER_TEXT], HugePageMeter_active_labels[i]);
      Meter_humanUnit(buffer, this->values[i], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[HUGEPAGE_1 + i], buffer);
   }
}

function: HugePageMeter_updateValues

static void HugePageMeter_updateValues(Meter* this) {
   assert(ARRAYSIZE(HugePageMeter_labels) == HTOP_HUGEPAGE_COUNT);

   char* buffer = this->txtBuffer;
   size_t size = sizeof(this->txtBuffer);
   int written;
   memory_t usedTotal = 0;
   unsigned nextUsed = 0;

   const LinuxMachine* host = (const LinuxMachine*) this->host;
   this->total = host->totalHugePageMem;
   this->values[0] = 0;
   HugePageMeter_active_labels[0] = " used:";
   for (unsigned i = 1; i < ARRAYSIZE(HugePageMeter_active_labels); i++) {
      this->values[i] = NAN;
      HugePageMeter_active_labels[i] = NULL;
   }
   for (unsigned i = 0; i < HTOP_HUGEPAGE_COUNT; i++) {
      memory_t value = host->usedHugePageMem[i];
      if (value != MEMORY_MAX) {
         this->values[nextUsed] = value;
         usedTotal += value;
         HugePageMeter_active_labels[nextUsed] = HugePageMeter_labels[i];
         if (++nextUsed == ARRAYSIZE(HugePageMeter_active_labels)) {
            break;
         }
      }
   }

   written = Meter_humanUnit(buffer, usedTotal, size);
   METER_BUFFER_CHECK(buffer, size, written);

   METER_BUFFER_APPEND_CHR(buffer, size, '/');

   Meter_humanUnit(buffer, this->total, size);
}

struct: HugePageMeter_class

const MeterClass HugePageMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = HugePageMeter_display,
   },
   .updateValues = HugePageMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = ARRAYSIZE(HugePageMeter_active_labels),
   .total = 100.0,
   .attributes = HugePageMeter_attributes,
   .name = "HugePages",
   .uiName = "HugePages",
   .caption = "HP"
};

HugePageMeter.h

linux/HugePageMeter.h

variable: HugePageMeter_class

extern const MeterClass HugePageMeter_class;

IOPriority.h

linux/IOPriority.h

enum: (anonymous enum)

enum {
   IOPRIO_CLASS_NONE,
   IOPRIO_CLASS_RT,
   IOPRIO_CLASS_BE,
   IOPRIO_CLASS_IDLE,
};

macro: IOPRIO_CLASS_SHIFT

#define IOPRIO_CLASS_SHIFT (13)

macro: IOPRIO_PRIO_MASK

#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1)

macro: IOPRIO_WHO_PROCESS

#define IOPRIO_WHO_PROCESS 1

macro: IOPriority_class

#define IOPriority_class(ioprio_) ((int) ((ioprio_) >> IOPRIO_CLASS_SHIFT) )

macro: IOPriority_data

#define IOPriority_data(ioprio_) ((int) ((ioprio_) & IOPRIO_PRIO_MASK) )

macro: IOPriority_Idle

#define IOPriority_Idle IOPriority_tuple(IOPRIO_CLASS_IDLE, 7)

macro: IOPriority_None

#define IOPriority_None IOPriority_tuple(IOPRIO_CLASS_NONE, 0)

macro: IOPriority_tuple

#define IOPriority_tuple(class_, data_) (((class_) << IOPRIO_CLASS_SHIFT) | (data_))

typedef: IOPriority

typedef int IOPriority;

IOPriorityPanel.c

linux/IOPriorityPanel.c

function: IOPriorityPanel_getIOPriority

IOPriority IOPriorityPanel_getIOPriority(Panel* this) {
   const ListItem* selected = (ListItem*) Panel_getSelected(this);
   return selected ? selected->key : IOPriority_None;
}

function: IOPriorityPanel_new

Panel* IOPriorityPanel_new(IOPriority currPrio) {
   Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Set    ", "Cancel "));

   Panel_setHeader(this, "IO Priority:");
   Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None));
   if (currPrio == IOPriority_None) {
      Panel_setSelected(this, 0);
   }
   static const struct {
      int klass;
      const char* name;
   } classes[] = {
      { .klass = IOPRIO_CLASS_RT, .name = "Realtime" },
      { .klass = IOPRIO_CLASS_BE, .name = "Best-effort" },
      { .klass = 0, .name = NULL }
   };
   for (int c = 0; classes[c].name; c++) {
      for (int i = 0; i < 8; i++) {
         char name[50];
         xSnprintf(name, sizeof(name), "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
         IOPriority ioprio = IOPriority_tuple(classes[c].klass, i);
         Panel_add(this, (Object*) ListItem_new(name, ioprio));
         if (currPrio == ioprio) {
            Panel_setSelected(this, Panel_size(this) - 1);
         }
      }
   }
   Panel_add(this, (Object*) ListItem_new("Idle", IOPriority_Idle));
   if (currPrio == IOPriority_Idle) {
      Panel_setSelected(this, Panel_size(this) - 1);
   }
   return this;
}

IOPriorityPanel.h

linux/IOPriorityPanel.h

function: IOPriorityPanel_getIOPriority

IOPriority IOPriorityPanel_getIOPriority(Panel* this);

function: IOPriorityPanel_new

Panel* IOPriorityPanel_new(IOPriority currPrio);

LibNl.c

linux/LibNl.c

function: handleNetlinkMsg

static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
   struct nlmsghdr* nlhdr;
   struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
   const struct nlattr* nlattr;
   struct taskstats stats;
   int rem;
   LinuxProcess* lp = (LinuxProcess*) linuxProcess;

   nlhdr = sym_nlmsg_hdr(nlmsg);

   if (sym_genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
      return NL_SKIP;
   }

   if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
      memcpy(&stats, sym_nla_data(sym_nla_next(sym_nla_data(nlattr), &rem)), sizeof(stats));
      assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid);

      // The xxx_delay_total values wrap around on overflow.
      // (Linux Kernel "Documentation/accounting/taskstats-struct.rst")
      unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
      #define DELTAPERC(x, y) (timeDelta ? MINIMUM((float)((x) - (y)) / timeDelta * 100.0F, 100.0F) : NAN)
      lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
      lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
      lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
      #undef DELTAPERC

      lp->swapin_delay_total = stats.swapin_delay_total;
      lp->blkio_delay_total = stats.blkio_delay_total;
      lp->cpu_delay_total = stats.cpu_delay_total;
      lp->delay_read_time = stats.ac_etime * 1000;
   }
   return NL_OK;
}

function: initNetlinkSocket

static void initNetlinkSocket(LinuxProcessTable* this) {
   if (load_libnl() < 0) {
      return;
   }

   this->netlink_socket = sym_nl_socket_alloc();
   if (this->netlink_socket == NULL) {
      return;
   }
   if (sym_nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
      return;
   }
   this->netlink_family = sym_genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
}

function: LibNl_destroyNetlinkSocket

void LibNl_destroyNetlinkSocket(LinuxProcessTable* this) {
   if (this->netlink_socket) {
      assert(libnlHandle);

      sym_nl_close(this->netlink_socket);
      sym_nl_socket_free(this->netlink_socket);
      this->netlink_socket = NULL;
   }

   unload_libnl();
}

function: LibNl_readDelayAcctData

void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) {
   struct nl_msg* msg;

   if (!this->netlink_socket) {
      initNetlinkSocket(this);
      if (!this->netlink_socket) {
         goto delayacct_failure;
      }
   }

   if (sym_nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
      goto delayacct_failure;
   }

   if (! (msg = sym_nlmsg_alloc())) {
      goto delayacct_failure;
   }

   if (! sym_genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
      sym_nlmsg_free(msg);
   }

   if (sym_nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) {
      sym_nlmsg_free(msg);
   }

   if (sym_nl_send_sync(this->netlink_socket, msg) < 0) {
      goto delayacct_failure;
   }

   if (sym_nl_recvmsgs_default(this->netlink_socket) < 0) {
      goto delayacct_failure;
   }

   return;

delayacct_failure:
   process->swapin_delay_percent = NAN;
   process->blkio_delay_percent = NAN;
   process->cpu_delay_percent = NAN;
}

function: load_libnl

static int load_libnl(void) {
   if (libnlHandle && libnlGenlHandle)
      return 0;

   libnlHandle = dlopen("libnl-3.so", RTLD_LAZY);
   if (!libnlHandle) {
      libnlHandle = dlopen("libnl-3.so.200", RTLD_LAZY);
      if (!libnlHandle) {
         goto dlfailure;
      }
   }

   libnlGenlHandle = dlopen("libnl-genl-3.so", RTLD_LAZY);
   if (!libnlGenlHandle) {
      libnlGenlHandle = dlopen("libnl-genl-3.so.200", RTLD_LAZY);
      if (!libnlGenlHandle) {
         goto dlfailure;
      }
   }

   /* Clear any errors */
   dlerror();

   #define resolve(handle, symbolname) do {                              \
      *(void **)(&sym_##symbolname) = dlsym(handle, #symbolname);        \
      if (!sym_##symbolname || dlerror() != NULL) {                      \
         goto dlfailure;                                                 \
      }                                                                  \
   } while(0)

   resolve(libnlHandle, nl_close);
   resolve(libnlHandle, nl_connect);
   resolve(libnlHandle, nl_recvmsgs_default);
   resolve(libnlHandle, nl_send_sync);
   resolve(libnlHandle, nl_socket_alloc);
   resolve(libnlHandle, nl_socket_free);
   resolve(libnlHandle, nl_socket_modify_cb);
   resolve(libnlHandle, nla_data);
   resolve(libnlHandle, nla_next);
   resolve(libnlHandle, nla_put_u32);
   resolve(libnlHandle, nlmsg_alloc);
   resolve(libnlHandle, nlmsg_free);
   resolve(libnlHandle, nlmsg_hdr);

   resolve(libnlGenlHandle, genl_ctrl_resolve);
   resolve(libnlGenlHandle, genlmsg_parse);
   resolve(libnlGenlHandle, genlmsg_put);

   #undef resolve

   return 0;

dlfailure:
   unload_libnl();
   return -1;
}

function: unload_libnl

static void unload_libnl(void) {
   sym_nl_close = NULL;
   sym_nl_connect = NULL;
   sym_nl_recvmsgs_default = NULL;
   sym_nl_send_sync = NULL;
   sym_nl_socket_alloc = NULL;
   sym_nl_socket_free = NULL;
   sym_nl_socket_modify_cb = NULL;
   sym_nla_data = NULL;
   sym_nla_next = NULL;
   sym_nla_put_u32 = NULL;
   sym_nlmsg_alloc = NULL;
   sym_nlmsg_free = NULL;
   sym_nlmsg_hdr = NULL;

   sym_genl_ctrl_resolve = NULL;
   sym_genlmsg_parse = NULL;
   sym_genlmsg_put = NULL;

   if (libnlGenlHandle) {
      dlclose(libnlGenlHandle);
      libnlGenlHandle = NULL;
   }
   if (libnlHandle) {
      dlclose(libnlHandle);
      libnlHandle = NULL;
   }
}

LibNl.h

linux/LibNl.h

function: LibNl_destroyNetlinkSocket

void LibNl_destroyNetlinkSocket(LinuxProcessTable* this);

function: LibNl_readDelayAcctData

void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process);

LibSensors.c

linux/LibSensors.c

function: LibSensors_cleanup

void LibSensors_cleanup(void) {
#ifdef BUILD_STATIC

   sym_sensors_cleanup();

#else

   if (dlopenHandle) {
      sym_sensors_cleanup();

      dlclose(dlopenHandle);
      dlopenHandle = NULL;
   }

#endif /* BUILD_STATIC */
}

function: LibSensors_countCCDs

int LibSensors_countCCDs(void) {

#ifndef BUILD_STATIC
   if (!dlopenHandle)
      return 0;
#endif /* !BUILD_STATIC */

   int ccds = 0;

   int n = 0;
   for (const sensors_chip_name* chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
      int m = 0;
      for (const sensors_feature* feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) {
         if (feature->type != SENSORS_FEATURE_TEMP)
            continue;

         if (!feature->name || !String_startsWith(feature->name, "temp"))
            continue;

         char *label = sym_sensors_get_label(chip, feature);
         if (label) {
            if (String_startsWith(label, "Tccd")) {
               ccds++;
            }
            free(label);
         }
      }
   }

   return ccds;
}

function: LibSensors_getCPUTemperatures

function: LibSensors_init

int LibSensors_init(void) {
#ifdef BUILD_STATIC

   return sym_sensors_init(NULL);

#else

   if (!dlopenHandle) {
      /* Find the unversioned libsensors.so (symlink) and prefer that, but Debian has .so.5 and Fedora .so.4 without
         matching symlinks (unless people install the -dev packages) */
      dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
      if (!dlopenHandle)
         dlopenHandle = dlopen("libsensors.so.5", RTLD_LAZY);
      if (!dlopenHandle)
         dlopenHandle = dlopen("libsensors.so.4", RTLD_LAZY);
      if (!dlopenHandle)
         goto dlfailure;

      /* Clear any errors */
      dlerror();

      #define resolve(symbolname) do {                                      \
         *(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname);  \
         if (!sym_##symbolname || dlerror() != NULL)                        \
            goto dlfailure;                                                 \
      } while(0)

      resolve(sensors_init);
      resolve(sensors_cleanup);
      resolve(sensors_get_detected_chips);
      resolve(sensors_get_features);
      resolve(sensors_get_subfeature);
      resolve(sensors_get_value);
      resolve(sensors_get_label);

      #undef resolve
   }

   return sym_sensors_init(NULL);


dlfailure:
   if (dlopenHandle) {
      dlclose(dlopenHandle);
      dlopenHandle = NULL;
   }
   return -1;

#endif /* BUILD_STATIC */
}

function: LibSensors_reload

int LibSensors_reload(void) {
#ifndef BUILD_STATIC
   if (!dlopenHandle) {
      errno = ENOTSUP;
      return -1;
   }
#endif /* !BUILD_STATIC */

   sym_sensors_cleanup();
   return sym_sensors_init(NULL);
}

function: tempDriverPriority

static int tempDriverPriority(const sensors_chip_name* chip) {
   static const struct TempDriverDefs {
      const char* prefix;
      int priority;
   } tempDrivers[] =  {
      { "coretemp",           0 },
      { "via_cputemp",        0 },
      { "cpu_thermal",        0 },
      { "k10temp",            0 },
      { "zenpower",           0 },
      /* Rockchip RK3588 */
      { "littlecore_thermal", 0 },
      { "bigcore0_thermal",   0 },
      { "bigcore1_thermal",   0 },
      { "bigcore2_thermal",   0 },
      /* Rockchip RK3566 */
      { "soc_thermal",        0 },
      /* Snapdragon 8cx */
      { "cpu0_thermal",       0 },
      { "cpu1_thermal",       0 },
      { "cpu2_thermal",       0 },
      { "cpu3_thermal",       0 },
      { "cpu4_thermal",       0 },
      { "cpu5_thermal",       0 },
      { "cpu6_thermal",       0 },
      { "cpu7_thermal",       0 },
      /* Low priority drivers */
      { "acpitz",             1 },
   };

   for (size_t i = 0; i < ARRAYSIZE(tempDrivers); i++)
      if (String_eq(chip->prefix, tempDrivers[i].prefix))
         return tempDrivers[i].priority;

   return -1;
}

LibSensors.h

linux/LibSensors.h

function: LibSensors_cleanup

void LibSensors_cleanup(void);

function: LibSensors_countCCDs

int LibSensors_countCCDs(void);

function: LibSensors_getCPUTemperatures

void LibSensors_getCPUTemperatures(CPUData* cpus, unsigned int existingCPUs, unsigned int activeCPUs);

function: LibSensors_init

int LibSensors_init(void);

function: LibSensors_reload

int LibSensors_reload(void);

LinuxMachine.c

linux/LinuxMachine.c

file: LinuxMachine.c

LinuxMachine.h

linux/LinuxMachine.h

macro: HTOP_HUGEPAGE_BASE_SHIFT

#define HTOP_HUGEPAGE_BASE_SHIFT 16

macro: HTOP_HUGEPAGE_COUNT

#define HTOP_HUGEPAGE_COUNT 24

macro: PROC_LINE_LENGTH

#define PROC_LINE_LENGTH 4096

macro: PROCARCSTATSFILE

#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats"

macro: PROCCPUINFOFILE

#define PROCCPUINFOFILE PROCDIR "/cpuinfo"

macro: PROCDIR

#define PROCDIR "/proc"

macro: PROCMEMINFOFILE

#define PROCMEMINFOFILE PROCDIR "/meminfo"

macro: PROCSTATFILE

#define PROCSTATFILE PROCDIR "/stat"

macro: PROCTTYDRIVERSFILE

#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"

struct: CPUData

typedef struct CPUData_ {
   unsigned long long int totalTime;
   unsigned long long int userTime;
   unsigned long long int systemTime;
   unsigned long long int systemAllTime;
   unsigned long long int idleAllTime;
   unsigned long long int idleTime;
   unsigned long long int niceTime;
   unsigned long long int ioWaitTime;
   unsigned long long int irqTime;
   unsigned long long int softIrqTime;
   unsigned long long int stealTime;
   unsigned long long int guestTime;

   unsigned long long int totalPeriod;
   unsigned long long int userPeriod;
   unsigned long long int systemPeriod;
   unsigned long long int systemAllPeriod;
   unsigned long long int idleAllPeriod;
   unsigned long long int idlePeriod;
   unsigned long long int nicePeriod;
   unsigned long long int ioWaitPeriod;
   unsigned long long int irqPeriod;
   unsigned long long int softIrqPeriod;
   unsigned long long int stealPeriod;
   unsigned long long int guestPeriod;

   double frequency;

   #ifdef HAVE_SENSORS_SENSORS_H
   double temperature;

   int physicalID;      /* different for each CPU socket */
   int coreID;          /* same for hyperthreading */
   int ccdID;           /* same for each AMD chiplet */
   #endif

   bool online;
} CPUData;

struct: GPUEngineData

typedef struct GPUEngineData_ {
   unsigned long long int prevTime, curTime;  /* absolute GPU time in nano seconds */
   char* key;                                 /* engine name */
   struct GPUEngineData_* next;
} GPUEngineData;

struct: LinuxMachine

typedef struct LinuxMachine_ {
   Machine super;

   long jiffies;
   int pageSize;
   int pageSizeKB;

   /* see Linux kernel source for further detail, fs/proc/stat.c */
   unsigned int runningTasks;   /* procs_running from /proc/stat */
   long long boottime;   /* btime field from /proc/stat */

   double period;

   CPUData* cpuData;

   #ifdef HAVE_SENSORS_SENSORS_H
   int maxPhysicalID;
   int maxCoreID;
   #endif

   memory_t totalHugePageMem;
   memory_t usedHugePageMem[HTOP_HUGEPAGE_COUNT];

   memory_t availableMem;

   unsigned long long int prevGpuTime, curGpuTime;  /* total absolute GPU time in nano seconds */
   GPUEngineData* gpuEngineData;

   ZfsArcStats zfs;
   ZramStats zram;
   ZswapStats zswap;
} LinuxMachine;

LinuxProcess.c

linux/LinuxProcess.c

function: LinuxProcess_changeAutogroupPriorityBy

static bool LinuxProcess_changeAutogroupPriorityBy(Process* p, Arg delta) {
   char buffer[256];
   pid_t pid = Process_getPid(p);
   xSnprintf(buffer, sizeof(buffer), PROCDIR "/%d/autogroup", pid);

   FILE* file = fopen(buffer, "r+");
   if (!file)
      return false;

   long int identity;
   int nice;
   int ok = fscanf(file, "/autogroup-%ld nice %d", &identity, &nice);
   bool success = false;
   if (ok == 2 && fseek(file, 0L, SEEK_SET) == 0) {
      xSnprintf(buffer, sizeof(buffer), "%d", nice + delta.i);
      success = fputs(buffer, file) > 0;
   }

   fclose(file);
   return success;
}

function: LinuxProcess_compareByKey

function: LinuxProcess_effectiveIOPriority

static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
   if (IOPriority_class(this->ioPriority) == IOPRIO_CLASS_NONE) {
      return IOPriority_tuple(IOPRIO_CLASS_BE, (this->super.nice + 20) / 5);
   }

   return this->ioPriority;
}

function: LinuxProcess_isAutogroupEnabled

bool LinuxProcess_isAutogroupEnabled(void) {
   char buf[16];
   if (xReadfile(PROCDIR "/sys/kernel/sched_autogroup_enabled", buf, sizeof(buf)) < 0)
      return false;
   return buf[0] == '1';
}

function: LinuxProcess_new

Process* LinuxProcess_new(const Machine* host) {
   LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
   Object_setClass(this, Class(LinuxProcess));
   Process_init(&this->super, host);
   return (Process*)this;
}

function: LinuxProcess_rowChangeAutogroupPriorityBy

bool LinuxProcess_rowChangeAutogroupPriorityBy(Row* super, Arg delta) {
   Process* p = (Process*) super;
   assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
   return LinuxProcess_changeAutogroupPriorityBy(p, delta);
}

function: LinuxProcess_rowSetIOPriority

bool LinuxProcess_rowSetIOPriority(Row* super, Arg ioprio) {
   Process* p = (Process*) super;
   assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
   return LinuxProcess_setIOPriority(p, ioprio);
}

function: LinuxProcess_rowWriteField

function: LinuxProcess_setIOPriority

static bool LinuxProcess_setIOPriority(Process* p, Arg ioprio) {
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_set
   syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, Process_getPid(p), ioprio.i);
#endif
   return LinuxProcess_updateIOPriority(p) == ioprio.i;
}

function: LinuxProcess_totalIORate

static double LinuxProcess_totalIORate(const LinuxProcess* lp) {
   double totalRate = NAN;
   if (isNonnegative(lp->io_rate_read_bps)) {
      totalRate = lp->io_rate_read_bps;
      if (isNonnegative(lp->io_rate_write_bps)) {
         totalRate += lp->io_rate_write_bps;
      }
   } else if (isNonnegative(lp->io_rate_write_bps)) {
      totalRate = lp->io_rate_write_bps;
   }
   return totalRate;
}

function: LinuxProcess_updateIOPriority

IOPriority LinuxProcess_updateIOPriority(Process* p) {
   IOPriority ioprio = 0;
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_get
   ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, Process_getPid(p));
#endif
   LinuxProcess* this = (LinuxProcess*) p;
   this->ioPriority = ioprio;
   return ioprio;
}

function: Process_delete

void Process_delete(Object* cast) {
   LinuxProcess* this = (LinuxProcess*) cast;
   Process_done((Process*)cast);
   free(this->container_short);
   free(this->cgroup_short);
   free(this->cgroup);
#ifdef HAVE_OPENVZ
   free(this->ctid);
#endif
   free(this->secattr);
   free(this);
}

global constant: Process_fields

struct: LinuxProcess_class

const ProcessClass LinuxProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = LinuxProcess_rowWriteField
   },
   .compareByKey = LinuxProcess_compareByKey
};

LinuxProcess.h

linux/LinuxProcess.h

function: LinuxProcess_isAutogroupEnabled

bool LinuxProcess_isAutogroupEnabled(void);

function: LinuxProcess_new

Process* LinuxProcess_new(const Machine* host);

function: Process_delete

void Process_delete(Object* cast);

method: LinuxProcess_rowChangeAutogroupPriorityBy

bool LinuxProcess_rowChangeAutogroupPriorityBy(Row* super, Arg delta);

method: LinuxProcess_rowSetIOPriority

bool LinuxProcess_rowSetIOPriority(Row* super, Arg ioprio);

method: LinuxProcess_updateIOPriority

IOPriority LinuxProcess_updateIOPriority(Process* proc);

method: Process_isThread

bool Process_isThread(const Process* this);

struct: LinuxProcess

LinuxProcessTable.c

linux/LinuxProcessTable.c

file: LinuxProcessTable.c

LinuxProcessTable.h

linux/LinuxProcessTable.h

struct: LinuxProcessTable

typedef struct LinuxProcessTable_ {
   ProcessTable super;

   TtyDriver* ttyDrivers;
   bool haveSmapsRollup;
   bool haveAutogroup;

   #ifdef HAVE_DELAYACCT
   struct nl_sock* netlink_socket;
   int netlink_family;
   #endif
} LinuxProcessTable;

struct: TtyDriver

typedef struct TtyDriver_ {
   char* path;
   unsigned int major;
   unsigned int minorFrom;
   unsigned int minorTo;
} TtyDriver;

Platform.c

linux/Platform.c

file: Platform.c

Platform.h

linux/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv);

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getPressureStall

void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

void Platform_longOptionsUsage(const char* name);

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this);

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this);

function: Platform_setZramValues

void Platform_setZramValues(Meter* this);

global variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

global variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

global variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

global variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

global variable: Platform_signals

extern const SignalItem Platform_signals[];

global variable: Running_containerized

extern bool Running_containerized;

macro: PATH_MAX

#ifndef PATH_MAX
   #define PATH_MAX 4096
#endif

macro: PLATFORM_LONG_OPTIONS

#ifdef HAVE_LIBCAP
   #define PLATFORM_LONG_OPTIONS \
      {"drop-capabilities", optional_argument, 0, 160},
#else
   #define PLATFORM_LONG_OPTIONS
#endif

PressureStallMeter.c

linux/PressureStallMeter.c

function: PressureStallMeter_display

static void PressureStallMeter_display(const Object* cast, RichString* out) {
   const Meter* this = (const Meter*)cast;
   char buffer[20];
   int len;

   len = xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[0]);
   RichString_appendnAscii(out, CRT_colors[PRESSURE_STALL_TEN], buffer, len);
   len = xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[1]);
   RichString_appendnAscii(out, CRT_colors[PRESSURE_STALL_SIXTY], buffer, len);
   len = xSnprintf(buffer, sizeof(buffer), "%5.2lf%% ", this->values[2]);
   RichString_appendnAscii(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer, len);
}

function: PressureStallMeter_updateValues

static void PressureStallMeter_updateValues(Meter* this) {
   const char* file;
   if (strstr(Meter_name(this), "CPU")) {
      file = "cpu";
   } else if (strstr(Meter_name(this), "IO")) {
      file = "io";
   } else if (strstr(Meter_name(this), "IRQ")) {
      file = "irq";
   } else {
      file = "memory";
   }

   bool some;
   if (strstr(Meter_name(this), "Some")) {
      some = true;
   } else {
      some = false;
   }

   Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]);

   /* only print bar for ten (not sixty and threehundred), cause the sum is meaningless */
   this->curItems = 1;

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s %s %5.2lf%% %5.2lf%% %5.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
}

global variable: PressureStallMeter_attributes

static const int PressureStallMeter_attributes[] = {
   PRESSURE_STALL_TEN,
   PRESSURE_STALL_SIXTY,
   PRESSURE_STALL_THREEHUNDRED
};

struct: PressureStallCPUSomeMeter_class

const MeterClass PressureStallCPUSomeMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = PressureStallMeter_display,
   },
   .updateValues = PressureStallMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = PressureStallMeter_attributes,
   .name = "PressureStallCPUSome",
   .uiName = "PSI some CPU",
   .caption = "PSI some CPU:    ",
   .description = "Pressure Stall Information, some cpu"
};

struct: PressureStallIOFullMeter_class

const MeterClass PressureStallIOFullMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = PressureStallMeter_display,
   },
   .updateValues = PressureStallMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = PressureStallMeter_attributes,
   .name = "PressureStallIOFull",
   .uiName = "PSI full IO",
   .caption = "PSI full IO:     ",
   .description = "Pressure Stall Information, full io"
};

struct: PressureStallIOSomeMeter_class

const MeterClass PressureStallIOSomeMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = PressureStallMeter_display,
   },
   .updateValues = PressureStallMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = PressureStallMeter_attributes,
   .name = "PressureStallIOSome",
   .uiName = "PSI some IO",
   .caption = "PSI some IO:     ",
   .description = "Pressure Stall Information, some io"
};

struct: PressureStallIRQFullMeter_class

const MeterClass PressureStallIRQFullMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = PressureStallMeter_display,
   },
   .updateValues = PressureStallMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = PressureStallMeter_attributes,
   .name = "PressureStallIRQFull",
   .uiName = "PSI full IRQ",
   .caption = "PSI full IRQ:    ",
   .description = "Pressure Stall Information, full irq"
};

struct: PressureStallMemoryFullMeter_class

const MeterClass PressureStallMemoryFullMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = PressureStallMeter_display,
   },
   .updateValues = PressureStallMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = PressureStallMeter_attributes,
   .name = "PressureStallMemoryFull",
   .uiName = "PSI full memory",
   .caption = "PSI full memory: ",
   .description = "Pressure Stall Information, full memory"
};

struct: PressureStallMemorySomeMeter_class

const MeterClass PressureStallMemorySomeMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = PressureStallMeter_display,
   },
   .updateValues = PressureStallMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = PressureStallMeter_attributes,
   .name = "PressureStallMemorySome",
   .uiName = "PSI some memory",
   .caption = "PSI some memory: ",
   .description = "Pressure Stall Information, some memory"
};

PressureStallMeter.h

linux/PressureStallMeter.h

struct: PressureStallCPUSomeMeter_class

extern const MeterClass PressureStallCPUSomeMeter_class;

struct: PressureStallIOFullMeter_class

extern const MeterClass PressureStallIOFullMeter_class;

struct: PressureStallIOSomeMeter_class

extern const MeterClass PressureStallIOSomeMeter_class;

struct: PressureStallIRQFullMeter_class

extern const MeterClass PressureStallIRQFullMeter_class;

struct: PressureStallMemoryFullMeter_class

extern const MeterClass PressureStallMemoryFullMeter_class;

struct: PressureStallMemorySomeMeter_class

extern const MeterClass PressureStallMemorySomeMeter_class;

ProcessField.h

linux/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   CMINFLT = 11,                 \
   CMAJFLT = 13,                 \
   UTIME = 14,                   \
   STIME = 15,                   \
   CUTIME = 16,                  \
   CSTIME = 17,                  \
   M_SHARE = 41,                 \
   M_TRS = 42,                   \
   M_DRS = 43,                   \
   M_LRS = 44,                   \
   CTID = 100,                   \
   VPID = 101,                   \
   VXID = 102,                   \
   RCHAR = 103,                  \
   WCHAR = 104,                  \
   SYSCR = 105,                  \
   SYSCW = 106,                  \
   RBYTES = 107,                 \
   WBYTES = 108,                 \
   CNCLWB = 109,                 \
   IO_READ_RATE = 110,           \
   IO_WRITE_RATE = 111,          \
   IO_RATE = 112,                \
   CGROUP = 113,                 \
   OOM = 114,                    \
   IO_PRIORITY = 115,            \
   PERCENT_CPU_DELAY = 116,      \
   PERCENT_IO_DELAY = 117,       \
   PERCENT_SWAP_DELAY = 118,     \
   M_PSS = 119,                  \
   M_SWAP = 120,                 \
   M_PSSWP = 121,                \
   CTXT = 122,                   \
   SECATTR = 123,                \
   AUTOGROUP_ID = 127,           \
   AUTOGROUP_NICE = 128,         \
   CCGROUP = 129,                \
   CONTAINER = 130,              \
   M_PRIV = 131,                 \
   GPU_TIME = 132,               \
   GPU_PERCENT = 133,            \
   ISCONTAINER = 134,            \
   // End of list

SELinuxMeter.c

linux/SELinuxMeter.c

function: hasSELinuxMount

static bool hasSELinuxMount(void) {
   struct statfs sfbuf;
   int r = statfs("/sys/fs/selinux", &sfbuf);
   if (r != 0) {
      return false;
   }

   if ((uint32_t)sfbuf.f_type != /* SELINUX_MAGIC */ 0xf97cff8cU) {
      return false;
   }

   struct statvfs vfsbuf;
   r = statvfs("/sys/fs/selinux", &vfsbuf);
   if (r != 0 || (vfsbuf.f_flag & ST_RDONLY)) {
      return false;
   }

   return true;
}

function: isSelinuxEnabled

static bool isSelinuxEnabled(void) {
   return hasSELinuxMount() && (0 == access("/etc/selinux/config", F_OK));
}

function: isSelinuxEnforcing

static bool isSelinuxEnforcing(void) {
   if (!enabled) {
      return false;
   }

   char buf[20];
   ssize_t r = xReadfile("/sys/fs/selinux/enforce", buf, sizeof(buf));
   if (r < 0)
      return false;

   int enforce = 0;
   if (sscanf(buf, "%d", &enforce) != 1) {
      return false;
   }

   return !!enforce;
}

function: SELinuxMeter_updateValues

static void SELinuxMeter_updateValues(Meter* this) {
   enabled = isSelinuxEnabled();
   enforcing = isSelinuxEnforcing();

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s%s", enabled ? "enabled" : "disabled", enabled ? (enforcing ? "; mode: enforcing" : "; mode: permissive") : "");
}

struct: SELinuxMeter_class

const MeterClass SELinuxMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
   },
   .updateValues = SELinuxMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = SELinuxMeter_attributes,
   .name = "SELinux",
   .uiName = "SELinux",
   .description = "SELinux state overview",
   .caption = "SELinux: "
};

variable: enabled

static bool enabled = false;

variable: enforcing

static bool enforcing = false;

variable: SELinuxMeter_attributes

static const int SELinuxMeter_attributes[] = {
   METER_TEXT,
};

SELinuxMeter.h

linux/SELinuxMeter.h

variable: SELinuxMeter_class

extern const MeterClass SELinuxMeter_class;

SystemdMeter.c

linux/SystemdMeter.c

function: _SystemdMeter_display

static void _SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out, SystemdMeterContext_t* ctx) {
   char buffer[16];
   int len;
   int color = METER_VALUE_ERROR;

   if (ctx->systemState) {
      color = String_eq(ctx->systemState, "running") ? METER_VALUE_OK :
              String_eq(ctx->systemState, "degraded") ? METER_VALUE_ERROR : METER_VALUE_WARN;
   }
   RichString_writeAscii(out, CRT_colors[color], ctx->systemState ? ctx->systemState : "N/A");

   RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");

   if (ctx->nFailedUnits == INVALID_VALUE) {
      buffer[0] = '?';
      buffer[1] = '\0';
      len = 1;
   } else {
      len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nFailedUnits);
   }
   RichString_appendnAscii(out, zeroDigitColor(ctx->nFailedUnits), buffer, len);

   RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");

   if (ctx->nNames == INVALID_VALUE) {
      buffer[0] = '?';
      buffer[1] = '\0';
      len = 1;
   } else {
      len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nNames);
   }
   RichString_appendnAscii(out, valueDigitColor(ctx->nNames), buffer, len);

   RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");

   if (ctx->nJobs == INVALID_VALUE) {
      buffer[0] = '?';
      buffer[1] = '\0';
      len = 1;
   } else {
      len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nJobs);
   }
   RichString_appendnAscii(out, zeroDigitColor(ctx->nJobs), buffer, len);

   RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");

   if (ctx->nInstalledJobs == INVALID_VALUE) {
      buffer[0] = '?';
      buffer[1] = '\0';
      len = 1;
   } else {
      len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nInstalledJobs);
   }
   RichString_appendnAscii(out, valueDigitColor(ctx->nInstalledJobs), buffer, len);

   RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
}

function: SystemdMeter_display

static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
   _SystemdMeter_display(cast, out, &ctx_system);
}

function: SystemdMeter_done

static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
   SystemdMeterContext_t* ctx = String_eq(Meter_name(this), "SystemdUser") ? &ctx_user : &ctx_system;

   free(ctx->systemState);
   ctx->systemState = NULL;

#ifdef BUILD_STATIC
# ifdef HAVE_LIBSYSTEMD
   if (ctx->bus) {
      sym_sd_bus_unref(ctx->bus);
   }
   ctx->bus = NULL;
# endif /* HAVE_LIBSYSTEMD */
#else /* BUILD_STATIC */
   if (ctx->bus && dlopenHandle) {
      sym_sd_bus_unref(ctx->bus);
   }
   ctx->bus = NULL;

   if (!ctx_system.systemState && !ctx_user.systemState && dlopenHandle) {
      dlclose(dlopenHandle);
      dlopenHandle = NULL;
   }
#endif /* BUILD_STATIC */
}

function: SystemdMeter_updateValues

static void SystemdMeter_updateValues(Meter* this) {
   bool user = String_eq(Meter_name(this), "SystemdUser");
   SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;

   free(ctx->systemState);
   ctx->systemState = NULL;
   ctx->nFailedUnits = ctx->nInstalledJobs = ctx->nNames = ctx->nJobs = INVALID_VALUE;

#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
   if (updateViaLib(user) < 0)
      updateViaExec(user);
#else
   updateViaExec(user);
#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", ctx->systemState ? ctx->systemState : "???");
}

function: SystemdUserMeter_display

static void SystemdUserMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
   _SystemdMeter_display(cast, out, &ctx_user);
}

function: updateViaExec

function: updateViaLib

function: valueDigitColor

static int valueDigitColor(unsigned int value) {
   switch (value) {
      case 0:
         return CRT_colors[METER_VALUE_NOTICE];
      case INVALID_VALUE:
         return CRT_colors[METER_VALUE_ERROR];
      default:
         return CRT_colors[METER_VALUE];
   }
}

function: zeroDigitColor

static int zeroDigitColor(unsigned int value) {
   switch (value) {
      case 0:
         return CRT_colors[METER_VALUE];
      case INVALID_VALUE:
         return CRT_colors[METER_VALUE_ERROR];
      default:
         return CRT_colors[METER_VALUE_NOTICE];
   }
}

global variable: ctx_system

static SystemdMeterContext_t ctx_system;

global variable: ctx_user

static SystemdMeterContext_t ctx_user;

global variable: dlopenHandle

#ifndef BUILD_STATIC
static void* dlopenHandle = NULL;
#endif

global variable: sym_sd_bus_get_property_string

#ifndef BUILD_STATIC
static int (*sym_sd_bus_get_property_string)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char**);
#endif

global variable: sym_sd_bus_get_property_trivial

#ifndef BUILD_STATIC
static int (*sym_sd_bus_get_property_trivial)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char, void*);
#endif

global variable: sym_sd_bus_open_system

#ifndef BUILD_STATIC
typedef void sd_bus;
typedef void sd_bus_error;
static int (*sym_sd_bus_open_system)(sd_bus**);
#endif

global variable: sym_sd_bus_open_user

#ifndef BUILD_STATIC
static int (*sym_sd_bus_open_user)(sd_bus**);
#endif

global variable: sym_sd_bus_unref

#ifndef BUILD_STATIC
static sd_bus* (*sym_sd_bus_unref)(sd_bus*);
#endif

global variable: SystemdMeter_attributes

static const int SystemdMeter_attributes[] = {
   METER_VALUE
};

global variable: SystemdMeter_class

const MeterClass SystemdMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = SystemdMeter_display
   },
   .updateValues = SystemdMeter_updateValues,
   .done = SystemdMeter_done,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = SystemdMeter_attributes,
   .name = "Systemd",
   .uiName = "Systemd state",
   .description = "Systemd system state and unit overview",
   .caption = "Systemd: ",
};

global variable: SystemdUserMeter_class

const MeterClass SystemdUserMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = SystemdUserMeter_display
   },
   .updateValues = SystemdMeter_updateValues,
   .done = SystemdMeter_done,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = SystemdMeter_attributes,
   .name = "SystemdUser",
   .uiName = "Systemd user state",
   .description = "Systemd user state and unit overview",
   .caption = "Systemd User: ",
};

struct: SystemdMeterContext

typedef struct SystemdMeterContext {
#if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
   sd_bus* bus;
#endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
   char* systemState;
   unsigned int nFailedUnits;
   unsigned int nInstalledJobs;
   unsigned int nNames;
   unsigned int nJobs;
} SystemdMeterContext_t;

SystemdMeter.h

linux/SystemdMeter.h

class: SystemdMeter_class

extern const MeterClass SystemdMeter_class;

class: SystemdUserMeter_class

extern const MeterClass SystemdUserMeter_class;

ZramMeter.c

linux/ZramMeter.c

const struct MeterClass: ZramMeter_class

const MeterClass ZramMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = ZramMeter_display,
   },
   .updateValues = ZramMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = ZRAM_METER_ITEMCOUNT,
   .total = 100.0,
   .attributes = ZramMeter_attributes,
   .name = "Zram",
   .uiName = "Zram",
   .caption = "zrm"
};

static array: ZramMeter_attributes

static const int ZramMeter_attributes[ZRAM_METER_ITEMCOUNT] = {
   [ZRAM_METER_COMPRESSED] = ZRAM_COMPRESSED,
   [ZRAM_METER_UNCOMPRESSED] = ZRAM_UNCOMPRESSED,
};

static function: ZramMeter_display

static void ZramMeter_display(const Object* cast, RichString* out) {
   char buffer[50];
   const Meter* this = (const Meter*)cast;

   RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");

   Meter_humanUnit(buffer, this->total, sizeof(buffer));
   RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);

   Meter_humanUnit(buffer, this->values[ZRAM_METER_COMPRESSED], sizeof(buffer));
   RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
   RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);

   double uncompressed = this->values[ZRAM_METER_COMPRESSED] + this->values[ZRAM_METER_UNCOMPRESSED];
   Meter_humanUnit(buffer, uncompressed, sizeof(buffer));
   RichString_appendAscii(out, CRT_colors[METER_TEXT], " uncompressed:");
   RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
}

static function: ZramMeter_updateValues

static void ZramMeter_updateValues(Meter* this) {
   char* buffer = this->txtBuffer;
   size_t size = sizeof(this->txtBuffer);
   int written;

   Platform_setZramValues(this);

   written = Meter_humanUnit(buffer, this->values[ZRAM_METER_COMPRESSED], size);
   METER_BUFFER_CHECK(buffer, size, written);

   METER_BUFFER_APPEND_CHR(buffer, size, '(');

   double uncompressed = this->values[ZRAM_METER_COMPRESSED] + this->values[ZRAM_METER_UNCOMPRESSED];
   written = Meter_humanUnit(buffer, uncompressed, size);
   METER_BUFFER_CHECK(buffer, size, written);

   METER_BUFFER_APPEND_CHR(buffer, size, ')');

   METER_BUFFER_APPEND_CHR(buffer, size, '/');

   Meter_humanUnit(buffer, this->total, size);
}

ZramMeter.h

linux/ZramMeter.h

enum: ZramMeterValues

typedef enum {
   ZRAM_METER_COMPRESSED = 0,
   ZRAM_METER_UNCOMPRESSED = 1,
   ZRAM_METER_ITEMCOUNT = 2, // number of entries in this enum
} ZramMeterValues;

extern const MeterClass declaration: ZramMeter_class

extern const MeterClass ZramMeter_class;

ZramStats.h

linux/ZramStats.h

struct: ZramStats

typedef struct ZramStats_ {
   memory_t totalZram;
   memory_t usedZramComp;
   memory_t usedZramOrig;
} ZramStats;

ZswapStats.h

linux/ZswapStats.h

struct: ZswapStats

typedef struct ZswapStats_ {
   /* amount of RAM used by the zswap pool */
   memory_t usedZswapComp;
   /* amount of data stored inside the zswap pool */
   memory_t usedZswapOrig;
} ZswapStats;

ListItem.c

ListItem.c

function: ListItem_append

void ListItem_append(ListItem* this, const char* text) {
   size_t oldLen = strlen(this->value);
   size_t textLen = strlen(text);
   size_t newLen = oldLen + textLen;
   this->value = xRealloc(this->value, newLen + 1);
   memcpy(this->value + oldLen, text, textLen);
   this->value[newLen] = '\0';
}

function: ListItem_compare

int ListItem_compare(const void* cast1, const void* cast2) {
   const ListItem* obj1 = (const ListItem*) cast1;
   const ListItem* obj2 = (const ListItem*) cast2;
   return strcmp(obj1->value, obj2->value);
}

function: ListItem_delete

void ListItem_delete(Object* cast) {
   ListItem* this = (ListItem*)cast;
   free(this->value);
   free(this);
}

function: ListItem_display

void ListItem_display(const Object* cast, RichString* out) {
   const ListItem* const this = (const ListItem*)cast;
   assert (this != NULL);

   if (this->moving) {
      RichString_writeWide(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW
                           CRT_utf8 ? "↕ " :
#endif
                           "+ ");
   }
   RichString_appendWide(out, CRT_colors[DEFAULT_COLOR], this->value);
}

function: ListItem_init

void ListItem_init(ListItem* this, const char* value, int key) {
   this->value = xStrdup(value);
   this->key = key;
   this->moving = false;
}

function: ListItem_new

ListItem* ListItem_new(const char* value, int key) {
   ListItem* this = AllocThis(ListItem);
   ListItem_init(this, value, key);
   return this;
}

struct constant: ListItem_class

const ObjectClass ListItem_class = {
   .display = ListItem_display,
   .delete = ListItem_delete,
   .compare = ListItem_compare
};

ListItem.h

ListItem.h

class: ListItem_class

extern const ObjectClass ListItem_class;

function: ListItem_append

void ListItem_append(ListItem* this, const char* text);

function: ListItem_compare

int ListItem_compare(const void* cast1, const void* cast2);

function: ListItem_delete

void ListItem_delete(Object* cast);

function: ListItem_display

void ListItem_display(const Object* cast, RichString* out);

function: ListItem_getRef

static inline const char* ListItem_getRef(const ListItem* this) {
   return this->value;
}

function: ListItem_init

void ListItem_init(ListItem* this, const char* value, int key);

function: ListItem_new

ListItem* ListItem_new(const char* value, int key);

struct: ListItem

typedef struct ListItem_ {
   Object super;
   char* value;
   int key;
   bool moving;
} ListItem;

LoadAverageMeter.c

LoadAverageMeter.c

function: LoadAverageMeter_display

static void LoadAverageMeter_display(const Object* cast, RichString* out) {
   const Meter* this = (const Meter*)cast;
   char buffer[20];
   int len;

   len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
   RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_ONE], buffer, len);
   len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[1]);
   RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer, len);
   len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[2]);
   RichString_appendnAscii(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer, len);
}

function: LoadAverageMeter_updateValues

static void LoadAverageMeter_updateValues(Meter* this) {
   Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);

   // only show bar for 1min value
   this->curItems = 1;

   // change bar color and total based on value
   if (this->values[0] < 1.0) {
      this->curAttributes = OK_attributes;
      this->total = 1.0;
   } else if (this->values[0] < this->host->activeCPUs) {
      this->curAttributes = Medium_attributes;
      this->total = this->host->activeCPUs;
   } else {
      this->curAttributes = High_attributes;
      this->total = 2 * this->host->activeCPUs;
   }

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
}

function: LoadMeter_display

static void LoadMeter_display(const Object* cast, RichString* out) {
   const Meter* this = (const Meter*)cast;
   char buffer[20];
   int len;

   len = xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
   RichString_appendnAscii(out, CRT_colors[LOAD], buffer, len);
}

function: LoadMeter_updateValues

static void LoadMeter_updateValues(Meter* this) {
   double five, fifteen;
   Platform_getLoadAverage(&this->values[0], &five, &fifteen);

   // change bar color and total based on value
   if (this->values[0] < 1.0) {
      this->curAttributes = OK_attributes;
      this->total = 1.0;
   } else if (this->values[0] < this->host->activeCPUs) {
      this->curAttributes = Medium_attributes;
      this->total = this->host->activeCPUs;
   } else {
      this->curAttributes = High_attributes;
      this->total = 2 * this->host->activeCPUs;
   }

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.2f", this->values[0]);
}

global constant: High_attributes

static const int High_attributes[] = {
   METER_VALUE_ERROR
};

global constant: LoadAverageMeter_attributes

static const int LoadAverageMeter_attributes[] = {
   LOAD_AVERAGE_ONE,
   LOAD_AVERAGE_FIVE,
   LOAD_AVERAGE_FIFTEEN
};

global constant: LoadMeter_attributes

static const int LoadMeter_attributes[] = {
   LOAD
};

global constant: Medium_attributes

static const int Medium_attributes[] = {
   METER_VALUE_WARN
};

global constant: OK_attributes

static const int OK_attributes[] = {
   METER_VALUE_OK
};

struct constant (MeterClass): LoadAverageMeter_class

const MeterClass LoadAverageMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = LoadAverageMeter_display,
   },
   .updateValues = LoadAverageMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 3,
   .total = 100.0,
   .attributes = LoadAverageMeter_attributes,
   .name = "LoadAverage",
   .uiName = "Load average",
   .description = "Load averages: 1 minute, 5 minutes, 15 minutes",
   .caption = "Load average: "
};

struct constant (MeterClass): LoadMeter_class

const MeterClass LoadMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = LoadMeter_display,
   },
   .updateValues = LoadMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 1,
   .total = 100.0,
   .attributes = LoadMeter_attributes,
   .name = "Load",
   .uiName = "Load",
   .description = "Load: average of ready processes in the last minute",
   .caption = "Load: "
};

LoadAverageMeter.h

LoadAverageMeter.h

file: LoadAverageMeter.h

#ifndef HEADER_LoadAverageMeter
#define HEADER_LoadAverageMeter
/*
htop - LoadAverageMeter.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "Meter.h"


extern const MeterClass LoadAverageMeter_class;

extern const MeterClass LoadMeter_class;

#endif

Machine.c

Machine.c

function: Machine_addTable

static void Machine_addTable(Machine* this, Table* table) {
   /* check that this table has not been seen previously */
   for (size_t i = 0; i < this->tableCount; i++)
      if (this->tables[i] == table)
         return;

   size_t nmemb = this->tableCount + 1;
   Table** tables = xReallocArray(this->tables, nmemb, sizeof(Table*));
   tables[nmemb - 1] = table;
   this->tables = tables;
   this->tableCount++;
}

function: Machine_done

void Machine_done(Machine* this) {
#ifdef HAVE_LIBHWLOC
   if (this->topologyOk) {
      hwloc_topology_destroy(this->topology);
   }
#endif
   Object_delete(this->processTable);
   free(this->tables);
}

function: Machine_init

void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId) {
   this->usersTable = usersTable;
   this->userId = userId;

   this->htopUserId = getuid();

   // discover fixed column width limits
   Row_setPidColumnWidth(Platform_getMaxPid());

   // always maintain valid realtime timestamps
   Platform_gettime_realtime(&this->realtime, &this->realtimeMs);

#ifdef HAVE_LIBHWLOC
   this->topologyOk = false;
   if (hwloc_topology_init(&this->topology) == 0) {
      this->topologyOk =
         #if HWLOC_API_VERSION < 0x00020000
         /* try to ignore the top-level machine object type */
         0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_MACHINE) &&
         /* ignore caches, which don't add structure */
         0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CORE) &&
         0 == hwloc_topology_ignore_type_keep_structure(this->topology, HWLOC_OBJ_CACHE) &&
         0 == hwloc_topology_set_flags(this->topology, HWLOC_TOPOLOGY_FLAG_WHOLE_SYSTEM) &&
         #else
         0 == hwloc_topology_set_all_types_filter(this->topology, HWLOC_TYPE_FILTER_KEEP_STRUCTURE) &&
         #endif
         0 == hwloc_topology_load(this->topology);
   }
#endif
}

function: Machine_populateTablesFromSettings

void Machine_populateTablesFromSettings(Machine* this, Settings* settings, Table* processTable) {
   this->settings = settings;
   this->processTable = processTable;

   for (size_t i = 0; i < settings->nScreens; i++) {
      ScreenSettings* ss = settings->screens[i];
      Table* table = ss->table;
      if (!table)
         table = ss->table = processTable;
      if (i == 0)
         this->activeTable = table;

      Machine_addTable(this, table);
   }
}

function: Machine_scanTables

void Machine_scanTables(Machine* this) {
   // set scan timestamp
   static bool firstScanDone = false;

   if (firstScanDone) {
      this->prevMonotonicMs = this->monotonicMs;
      Platform_gettime_monotonic(&this->monotonicMs);
   } else {
      this->prevMonotonicMs = 0;
      this->monotonicMs = 1;
      firstScanDone = true;
   }
   if (this->monotonicMs <= this->prevMonotonicMs) {
      return;
   }

   this->maxUserId = 0;
   Row_resetFieldWidths();

   for (size_t i = 0; i < this->tableCount; i++) {
      Table* table = this->tables[i];

      // pre-processing of each row
      Table_scanPrepare(table);

      // scan values for this table
      Table_scanIterate(table);

      // post-process after scanning
      Table_scanCleanup(table);
   }

   Row_setUidColumnWidth(this->maxUserId);
}

function: Machine_setTablesPanel

void Machine_setTablesPanel(Machine* this, Panel* panel) {
   for (size_t i = 0; i < this->tableCount; i++) {
      Table_setPanel(this->tables[i], panel);
   }
}

Machine.h

Machine.h

function: Machine_delete

void Machine_delete(Machine* this);

function: Machine_done

void Machine_done(Machine* this);

function: Machine_init

void Machine_init(Machine* this, UsersTable* usersTable, uid_t userId);

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* this, unsigned int id);

function: Machine_new

Machine* Machine_new(UsersTable* usersTable, uid_t userId);

function: Machine_populateTablesFromSettings

void Machine_populateTablesFromSettings(Machine* this, Settings* settings, Table* processTable);

function: Machine_scan

void Machine_scan(Machine* this);

function: Machine_scanTables

void Machine_scanTables(Machine* this);

function: Machine_setTablesPanel

void Machine_setTablesPanel(Machine* this, Panel* panel);

macro: MAX_NAME

#define MAX_NAME 128

macro: MAX_READ

#define MAX_READ 2048

macro: MEMORY_MAX

#define MEMORY_MAX ULLONG_MAX

struct: Machine

typedef struct Machine_ {
   struct Settings_* settings;

   struct timeval realtime;   /* time of the current sample */
   uint64_t realtimeMs;       /* current time in milliseconds */
   uint64_t monotonicMs;      /* same, but from monotonic clock */
   uint64_t prevMonotonicMs;  /* time in milliseconds from monotonic clock of previous scan */

   int64_t iterationsRemaining;

   #ifdef HAVE_LIBHWLOC
   hwloc_topology_t topology;
   bool topologyOk;
   #endif

   memory_t totalMem;
   memory_t usedMem;
   memory_t buffersMem;
   memory_t cachedMem;
   memory_t sharedMem;
   memory_t availableMem;

   memory_t totalSwap;
   memory_t usedSwap;
   memory_t cachedSwap;

   unsigned int activeCPUs;
   unsigned int existingCPUs;

   UsersTable* usersTable;
   uid_t htopUserId;
   uid_t maxUserId;  /* recently observed */
   uid_t userId;  /* selected row user ID */

   size_t tableCount;
   Table **tables;
   Table *activeTable;
   Table *processTable;
} Machine;

typedef: memory_t

typedef unsigned long long int memory_t;

Macros.h

Macros.h

function: isNaN

/* Cheaper function for checking NaNs. Unlike the standard isnan(), this may
   throw an FP exception on a "signaling NaN".
   (ISO/IEC TS 18661-1 and the C23 standard stated that isnan() throws no
   exceptions even with a "signaling NaN") */
static inline bool isNaN(double x) {
   return !isgreaterequal(x, x);
}

function: isNonnegative

/* Checks if x >= 0.0 but returns false if x is NaN. Because IEEE 754 considers
   -0.0 == 0.0, this function treats both zeros as nonnegative. */
static inline bool isNonnegative(double x) {
   return isgreaterequal(x, 0.0);
}

function: isPositive

/* Checks if x > 0.0 but returns false if x is NaN. */
static inline bool isPositive(double x) {
   return isgreater(x, 0.0);
}

function: saturatingSub

/* This subtraction is used by Linux / NetBSD / OpenBSD for calculation of CPU usage items. */
static inline unsigned long long saturatingSub(unsigned long long a, unsigned long long b) {
   return a > b ? a - b : 0;
}

macro: ARRAYSIZE

#define ARRAYSIZE(x)                   (sizeof(x) / sizeof((x)[0]))

macro: ATTR_ACCESS2

#define ATTR_ACCESS2(mode, ref)         __attribute__((access (mode, ref)))

macro: ATTR_ACCESS2_R

#define ATTR_ACCESS2_R(ref)              ATTR_ACCESS2(read_only, ref)

macro: ATTR_ACCESS2_RW

#define ATTR_ACCESS2_RW(ref)             ATTR_ACCESS2(read_write, ref)

macro: ATTR_ACCESS2_W

#define ATTR_ACCESS2_W(ref)              ATTR_ACCESS2(write_only, ref)

macro: ATTR_ACCESS3

#define ATTR_ACCESS3(mode, ref, size)   __attribute__((access (mode, ref, size)))

macro: ATTR_ACCESS3_R

#define ATTR_ACCESS3_R(ref, size)        ATTR_ACCESS3(read_only, ref, size)

macro: ATTR_ACCESS3_RW

#define ATTR_ACCESS3_RW(ref, size)       ATTR_ACCESS3(read_write, ref, size)

macro: ATTR_ACCESS3_W

#define ATTR_ACCESS3_W(ref, size)        ATTR_ACCESS3(write_only, ref, size)

macro: ATTR_ALLOC_SIZE1

#define ATTR_ALLOC_SIZE1(a)             __attribute__((alloc_size (a)))

macro: ATTR_ALLOC_SIZE2

#define ATTR_ALLOC_SIZE2(a, b)          __attribute__((alloc_size (a, b)))

macro: ATTR_FORMAT

#define ATTR_FORMAT(type, index, check) __attribute__((format (type, index, check)))

macro: ATTR_MALLOC

#define ATTR_MALLOC                     __attribute__((malloc))

macro: ATTR_NONNULL

#define ATTR_NONNULL                    __attribute__((nonnull))

macro: ATTR_NONNULL_N

#define ATTR_NONNULL_N(...)             __attribute__((nonnull(__VA_ARGS__)))

macro: ATTR_NORETURN

#define ATTR_NORETURN                   __attribute__((noreturn))

macro: ATTR_RETNONNULL

#define ATTR_RETNONNULL                 __attribute__((returns_nonnull))

macro: ATTR_UNUSED

#define ATTR_UNUSED                     __attribute__((unused))

macro: CLAMP

#define CLAMP(x, low, high)            (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low))

macro: IGNORE_WCASTQUAL_BEGIN

#define IGNORE_WCASTQUAL_BEGIN  _Pragma("clang diagnostic push") \
                                _Pragma("clang diagnostic ignored \"-Wcast-qual\"")

macro: IGNORE_WCASTQUAL_END

#define IGNORE_WCASTQUAL_END    _Pragma("clang diagnostic pop")

macro: MAXIMUM

#define MAXIMUM(a, b)                  ((a) > (b) ? (a) : (b))

macro: MINIMUM

#define MINIMUM(a, b)                  ((a) < (b) ? (a) : (b))

macro: SPACESHIP_DEFAULTSTR

#define SPACESHIP_DEFAULTSTR(a, b, s)  strcmp((a) ? (a) : (s), (b) ? (b) : (s))

macro: SPACESHIP_NULLSTR

#define SPACESHIP_NULLSTR(a, b)        strcmp((a) ? (a) : "", (b) ? (b) : "")

macro: SPACESHIP_NUMBER

#define SPACESHIP_NUMBER(a, b)         (((a) > (b)) - ((a) < (b)))

MainPanel.c

MainPanel.c

const_array: MainFunctions

static const char* const MainFunctions[]     = {"Help  ", "Setup ", "Search", "Filter", "Tree  ", "SortBy", "Nice -", "Nice +", "Kill  ", "Quit  ", NULL};

const_array: MainFunctions_ro

static const char* const MainFunctions_ro[]  = {"Help  ", "Setup ", "Search", "Filter", "Tree  ", "SortBy", "      ", "      ", "      ", "Quit  ", NULL};

function: MainPanel_delete

void MainPanel_delete(Object* object) {
   MainPanel* this = (MainPanel*) object;
   MainPanel_setFunctionBar(this, false);
   FunctionBar_delete(this->readonlyBar);
   IncSet_delete(this->inc);
   free(this->keys);
   Panel_done(&this->super);
   free(this);
}

function: MainPanel_drawFunctionBar

static void MainPanel_drawFunctionBar(Panel* super, bool hideFunctionBar) {
   MainPanel* this = (MainPanel*) super;

   // Do not hide active search and filter bar.
   if (hideFunctionBar && !this->inc->active)
      return;

   IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
   if (this->state->pauseUpdate) {
      FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
   }
}

function: MainPanel_eventHandler

function: MainPanel_foreachRow

bool MainPanel_foreachRow(MainPanel* this, MainPanel_foreachRowFn fn, Arg arg, bool* wasAnyTagged) {
   Panel* super = &this->super;
   bool ok = true;
   bool anyTagged = false;
   for (int i = 0; i < Panel_size(super); i++) {
      Row* row = (Row*) Panel_get(super, i);
      if (row->tag) {
         ok &= fn(row, arg);
         anyTagged = true;
      }
   }
   if (!anyTagged) {
      Row* row = (Row*) Panel_getSelected(super);
      if (row) {
         ok &= fn(row, arg);
      }
   }

   if (wasAnyTagged)
      *wasAnyTagged = anyTagged;

   return ok;
}

function: MainPanel_getValue

static const char* MainPanel_getValue(Panel* this, int i) {
   Row* row = (Row*) Panel_get(this, i);
   return Row_sortKeyString(row);
}

function: MainPanel_idSearch

static void MainPanel_idSearch(MainPanel* this, int ch) {
   Panel* super = &this->super;
   pid_t id = ch - 48 + this->idSearch;
   for (int i = 0; i < Panel_size(super); i++) {
      const Row* row = (const Row*) Panel_get(super, i);
      if (row && row->id == id) {
         Panel_setSelected(super, i);
         break;
      }
   }
   this->idSearch = id * 10;
   if (this->idSearch > 10000000) {
      this->idSearch = 0;
   }
}

function: MainPanel_new

MainPanel* MainPanel_new(void) {
   MainPanel* this = AllocThis(MainPanel);
   this->processBar = FunctionBar_new(MainFunctions, NULL, NULL);
   this->readonlyBar = FunctionBar_new(MainFunctions_ro, NULL, NULL);
   FunctionBar* activeBar = Settings_isReadonly() ? this->readonlyBar : this->processBar;
   Panel_init((Panel*) this, 1, 1, 1, 1, Class(Row), false, activeBar);
   this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action));
   this->inc = IncSet_new(activeBar);

   Action_setBindings(this->keys);
   Platform_setBindings(this->keys);

   return this;
}

function: MainPanel_printHeader

static void MainPanel_printHeader(Panel* super) {
   MainPanel* this = (MainPanel*) super;
   Machine* host = this->state->host;
   Table_printHeader(host->settings, &super->header);
}

function: MainPanel_selectedRow

int MainPanel_selectedRow(MainPanel* this) {
   const Row* row = (const Row*) Panel_getSelected((Panel*)this);
   return row ? row->id : -1;
}

function: MainPanel_setFunctionBar

void MainPanel_setFunctionBar(MainPanel* this, bool readonly) {
   this->super.defaultBar = readonly ? this->readonlyBar : this->processBar;
   this->inc->defaultBar = this->super.defaultBar;
}

function: MainPanel_setState

void MainPanel_setState(MainPanel* this, State* state) {
   this->state = state;
}

function: MainPanel_updateLabels

void MainPanel_updateLabels(MainPanel* this, bool list, bool filter) {
   FunctionBar* bar = MainPanel_getFunctionBar(this);
   FunctionBar_setLabel(bar, KEY_F(5), list   ? "List  " : "Tree  ");
   FunctionBar_setLabel(bar, KEY_F(4), filter ? "FILTER" : "Filter");
}

struct_instance: MainPanel_class

const PanelClass MainPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = MainPanel_delete
   },
   .eventHandler = MainPanel_eventHandler,
   .drawFunctionBar = MainPanel_drawFunctionBar,
   .printHeader = MainPanel_printHeader
};

MainPanel.h

MainPanel.h

extern const: MainPanel_class

extern const PanelClass MainPanel_class;

function: MainPanel_delete

void MainPanel_delete(Object* object);

function: MainPanel_foreachRow

bool MainPanel_foreachRow(MainPanel* this, MainPanel_foreachRowFn fn, Arg arg, bool* wasAnyTagged);

function: MainPanel_new

MainPanel* MainPanel_new(void);

function: MainPanel_selectedRow

int MainPanel_selectedRow(MainPanel* this);

function: MainPanel_setFunctionBar

void MainPanel_setFunctionBar(MainPanel* this, bool readonly);

function: MainPanel_setState

void MainPanel_setState(MainPanel* this, State* state);

function: MainPanel_updateLabels

void MainPanel_updateLabels(MainPanel* this, bool list, bool filter);

macro: MainPanel_getFunctionBar

#define MainPanel_getFunctionBar(this_) (((Panel*)(this_))->defaultBar)

struct: MainPanel_

typedef struct MainPanel_ {
   Panel super;
   State* state;
   IncSet* inc;
   Htop_Action* keys;
   FunctionBar* processBar;  /* function bar with process-specific actions */
   FunctionBar* readonlyBar;  /* function bar without process actions (ro) */
   unsigned int idSearch;
} MainPanel;

typedef: MainPanel_foreachRowFn

typedef bool(*MainPanel_foreachRowFn)(Row*, Arg);

Makefile.am

Makefile.am

file: Makefile.am

MemoryMeter.c

MemoryMeter.c

function: MemoryMeter_display

function: MemoryMeter_updateValues

static void MemoryMeter_updateValues(Meter* this) {
   char* buffer = this->txtBuffer;
   size_t size = sizeof(this->txtBuffer);
   int written;

   Settings *settings = this->host->settings;

   /* shared, compressed and available memory are not supported on all platforms */
   this->values[MEMORY_METER_SHARED] = NAN;
   this->values[MEMORY_METER_COMPRESSED] = NAN;
   this->values[MEMORY_METER_AVAILABLE] = NAN;
   Platform_setMemoryValues(this);
   if ((this->mode == GRAPH_METERMODE || this->mode == BAR_METERMODE) && !settings->showCachedMemory) {
      this->values[MEMORY_METER_BUFFERS] = 0;
      this->values[MEMORY_METER_CACHE] = 0;
   }
   /* Do not print available memory in bar mode */
   static_assert(MEMORY_METER_AVAILABLE + 1 == MEMORY_METER_ITEMCOUNT,
      "MEMORY_METER_AVAILABLE is not the last item in MemoryMeterValues");
   this->curItems = MEMORY_METER_AVAILABLE;

   /* we actually want to show "used + shared + compressed" */
   double used = this->values[MEMORY_METER_USED];
   if (isPositive(this->values[MEMORY_METER_SHARED]))
      used += this->values[MEMORY_METER_SHARED];
   if (isPositive(this->values[MEMORY_METER_COMPRESSED]))
      used += this->values[MEMORY_METER_COMPRESSED];

   written = Meter_humanUnit(buffer, used, size);
   METER_BUFFER_CHECK(buffer, size, written);

   METER_BUFFER_APPEND_CHR(buffer, size, '/');

   Meter_humanUnit(buffer, this->total, size);
}

global variable: MemoryMeter_attributes

static const int MemoryMeter_attributes[] = {
   MEMORY_USED,
   MEMORY_SHARED,
   MEMORY_COMPRESSED,
   MEMORY_BUFFERS,
   MEMORY_CACHE
};

global variable: MemoryMeter_class

const MeterClass MemoryMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = MemoryMeter_display,
   },
   .updateValues = MemoryMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = MEMORY_METER_ITEMCOUNT,
   .total = 100.0,
   .attributes = MemoryMeter_attributes,
   .name = "Memory",
   .uiName = "Memory",
   .caption = "Mem"
};

MemoryMeter.h

MemoryMeter.h

declaration: MemoryMeter_class

extern const MeterClass MemoryMeter_class;

enum: MemoryMeterValues

typedef enum {
   MEMORY_METER_USED = 0,
   MEMORY_METER_SHARED = 1,
   MEMORY_METER_COMPRESSED = 2,
   MEMORY_METER_BUFFERS = 3,
   MEMORY_METER_CACHE = 4,
   MEMORY_METER_AVAILABLE = 5,
   MEMORY_METER_ITEMCOUNT = 6, // number of entries in this enum
} MemoryMeterValues;

MemorySwapMeter.c

MemorySwapMeter.c

function: MemorySwapMeter_done

static void MemorySwapMeter_done(Meter* this) {
   MemorySwapMeterData* data = this->meterData;

   Meter_delete((Object*)data->swapMeter);
   Meter_delete((Object*)data->memoryMeter);

   free(data);
}

function: MemorySwapMeter_draw

static void MemorySwapMeter_draw(Meter* this, int x, int y, int w) {
   MemorySwapMeterData* data = this->meterData;

   /* Use the same width for each sub meter to align with CPU meter */
   const int colwidth = w / 2;
   const int diff = w - colwidth * 2;

   assert(data->memoryMeter->draw);
   data->memoryMeter->draw(data->memoryMeter, x, y, colwidth);
   assert(data->swapMeter->draw);
   data->swapMeter->draw(data->swapMeter, x + colwidth + diff, y, colwidth);
}

function: MemorySwapMeter_init

static void MemorySwapMeter_init(Meter* this) {
   MemorySwapMeterData* data = this->meterData;

   if (!data) {
      data = this->meterData = xCalloc(1, sizeof(MemorySwapMeterData));
   }

   if (!data->memoryMeter)
      data->memoryMeter = Meter_new(this->host, 0, (const MeterClass*) Class(MemoryMeter));
   if (!data->swapMeter)
      data->swapMeter = Meter_new(this->host, 0, (const MeterClass*) Class(SwapMeter));

   if (Meter_initFn(data->memoryMeter)) {
      Meter_init(data->memoryMeter);
   }
   if (Meter_initFn(data->swapMeter)) {
      Meter_init(data->swapMeter);
   }
}

function: MemorySwapMeter_updateMode

static void MemorySwapMeter_updateMode(Meter* this, MeterModeId mode) {
   MemorySwapMeterData* data = this->meterData;

   this->mode = mode;

   Meter_setMode(data->memoryMeter, mode);
   Meter_setMode(data->swapMeter, mode);

   this->h = MAXIMUM(data->memoryMeter->h, data->swapMeter->h);
}

function: MemorySwapMeter_updateValues

static void MemorySwapMeter_updateValues(Meter* this) {
   MemorySwapMeterData* data = this->meterData;

   Meter_updateValues(data->memoryMeter);
   Meter_updateValues(data->swapMeter);
}

global constant: MemorySwapMeter_class

const MeterClass MemorySwapMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
   },
   .updateValues = MemorySwapMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .isMultiColumn = true,
   .name = "MemorySwap",
   .uiName = "Memory & Swap",
   .description = "Combined memory and swap usage",
   .caption = "M&S",
   .draw = MemorySwapMeter_draw,
   .init = MemorySwapMeter_init,
   .updateMode = MemorySwapMeter_updateMode,
   .done = MemorySwapMeter_done
};

struct: MemorySwapMeterData

typedef struct MemorySwapMeterData_ {
   Meter* memoryMeter;
   Meter* swapMeter;
} MemorySwapMeterData;

MemorySwapMeter.h

MemorySwapMeter.h

variable: MemorySwapMeter_class

extern const MeterClass MemorySwapMeter_class;

Meter.c

Meter.c

function: BarMeterMode_draw

function: BlankMeter_display

static void BlankMeter_display(ATTR_UNUSED const Object* cast, ATTR_UNUSED RichString* out) {
}

function: BlankMeter_updateValues

static void BlankMeter_updateValues(Meter* this) {
   this->txtBuffer[0] = '\0';
}

function: GraphMeterMode_draw

function: LEDMeterMode_draw

static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
#ifdef HAVE_LIBNCURSESW
   if (CRT_utf8)
      LEDMeterMode_digits = LEDMeterMode_digitsUtf8;
   else
#endif
      LEDMeterMode_digits = LEDMeterMode_digitsAscii;

   RichString_begin(out);
   Meter_displayBuffer(this, &out);

   int yText =
#ifdef HAVE_LIBNCURSESW
      CRT_utf8 ? y + 1 :
#endif
      y + 2;
   attrset(CRT_colors[LED_COLOR]);
   const char* caption = Meter_getCaption(this);
   mvaddstr(yText, x, caption);
   int xx = x + strlen(caption);
   int len = RichString_sizeVal(out);
   for (int i = 0; i < len; i++) {
      int c = RichString_getCharVal(out, i);
      if (c >= '0' && c <= '9') {
         if (xx - x + 4 > w)
            break;

         LEDMeterMode_drawDigit(xx, y, c - '0');
         xx += 4;
      } else {
         if (xx - x + 1 > w)
            break;
#ifdef HAVE_LIBNCURSESW
         const cchar_t wc = { .chars = { c, '\0' }, .attr = 0 }; /* use LED_COLOR from attrset() */
         mvadd_wch(yText, xx, &wc);
#else
         mvaddch(yText, xx, c);
#endif
         xx += 1;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
   RichString_delete(&out);
}

function: LEDMeterMode_drawDigit

static void LEDMeterMode_drawDigit(int x, int y, int n) {
   for (int i = 0; i < 3; i++)
      mvaddstr(y + i, x, LEDMeterMode_digits[i * 10 + n]);
}

function: Meter_delete

void Meter_delete(Object* cast) {
   if (!cast)
      return;

   Meter* this = (Meter*) cast;
   if (Meter_doneFn(this)) {
      Meter_done(this);
   }
   free(this->drawData.values);
   free(this->caption);
   free(this->values);
   free(this);
}

function: Meter_displayBuffer

static inline void Meter_displayBuffer(const Meter* this, RichString* out) {
   if (Object_displayFn(this)) {
      Object_display(this, out);
   } else {
      RichString_writeWide(out, CRT_colors[Meter_attributes(this)[0]], this->txtBuffer);
   }
}

function: Meter_humanUnit

int Meter_humanUnit(char* buffer, double value, size_t size) {
   size_t i = 0;

   assert(value >= 0.0);
   while (value >= ONE_K) {
      if (i >= ARRAYSIZE(unitPrefixes) - 1) {
         if (value > 9999.0) {
            return xSnprintf(buffer, size, "inf");
         }
         break;
      }

      value /= ONE_K;
      ++i;
   }

   int precision = 0;

   if (i > 0) {
      // Fraction digits for mebibytes and above
      precision = value <= 99.9 ? (value <= 9.99 ? 2 : 1) : 0;

      // Round up if 'value' is in range (99.9, 100) or (9.99, 10)
      if (precision < 2) {
         double limit = precision == 1 ? 10.0 : 100.0;
         if (value < limit) {
            value = limit;
         }
      }
   }

   return xSnprintf(buffer, size, "%.*f%c", precision, value, unitPrefixes[i]);
}

function: Meter_new

Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type) {
   Meter* this = xCalloc(1, sizeof(Meter));
   Object_setClass(this, type);
   this->h = 1;
   this->param = param;
   this->host = host;
   this->curItems = type->maxItems;
   this->curAttributes = NULL;
   this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
   this->total = type->total;
   this->caption = xStrdup(type->caption);
   if (Meter_initFn(this)) {
      Meter_init(this);
   }

   Meter_setMode(this, type->defaultMode);
   assert(this->mode > 0);
   return this;
}

function: Meter_nextSupportedMode

MeterModeId Meter_nextSupportedMode(const Meter* this) {
   uint32_t supportedModes = Meter_supportedModes(this);
   assert(supportedModes);

   assert(this->mode < UINT32_WIDTH);
   uint32_t modeMask = ((uint32_t)-1 << 1) << this->mode;
   uint32_t nextModes = supportedModes & modeMask;
   if (!nextModes) {
      nextModes = supportedModes;
   }

   return (MeterModeId)countTrailingZeros(nextModes);
}

function: Meter_setCaption

void Meter_setCaption(Meter* this, const char* caption) {
   free_and_xStrdup(&this->caption, caption);
}

function: Meter_setMode

void Meter_setMode(Meter* this, MeterModeId modeIndex) {
   if (modeIndex == this->mode) {
      assert(this->mode > 0);
      return;
   }

   uint32_t supportedModes = Meter_supportedModes(this);
   assert(supportedModes);
   assert(!(supportedModes & (1 << 0)));

   assert(LAST_METERMODE <= UINT32_WIDTH);
   if (modeIndex >= LAST_METERMODE || !(supportedModes & (1UL << modeIndex)))
      return;

   assert(modeIndex >= 1);
   if (Meter_updateModeFn(this)) {
      assert(Meter_drawFn(this));
      this->draw = Meter_drawFn(this);
      Meter_updateMode(this, modeIndex);
   } else {
      free(this->drawData.values);
      this->drawData.values = NULL;
      this->drawData.nValues = 0;

      const MeterMode* mode = &Meter_modes[modeIndex];
      this->draw = mode->draw;
      this->h = mode->h;
   }
   this->mode = modeIndex;
}

function: Meter_toListItem

ListItem* Meter_toListItem(const Meter* this, bool moving) {
   char mode[20];
   if (this->mode > 0) {
      xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode].uiName);
   } else {
      mode[0] = '\0';
   }
   char name[32];
   if (Meter_getUiNameFn(this))
      Meter_getUiName(this, name, sizeof(name));
   else
      xSnprintf(name, sizeof(name), "%s", Meter_uiName(this));
   char buffer[50];
   xSnprintf(buffer, sizeof(buffer), "%s%s", name, mode);
   ListItem* li = ListItem_new(buffer, 0);
   li->moving = moving;
   return li;
}

function: TextMeterMode_draw

static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
   const char* caption = Meter_getCaption(this);
   attrset(CRT_colors[METER_TEXT]);
   mvaddnstr(y, x, caption, w);
   attrset(CRT_colors[RESET_COLOR]);

   int captionLen = strlen(caption);
   x += captionLen;
   w -= captionLen;
   if (w <= 0)
      return;

   RichString_begin(out);
   Meter_displayBuffer(this, &out);
   RichString_printoffnVal(out, y, x, 0, w);
   RichString_delete(&out);
}

global variable: BarMeterMode_characters

static const char BarMeterMode_characters[] = "|#*@$%&.";

global variable: BlankMeter_attributes

static const int BlankMeter_attributes[] = {
   DEFAULT_COLOR
};

global variable: BlankMeter_class

const MeterClass BlankMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = BlankMeter_display,
   },
   .updateValues = BlankMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = BlankMeter_attributes,
   .name = "Blank",
   .uiName = "Blank",
   .caption = ""
};

global variable: GraphMeterMode_dotsAscii

static const char* const GraphMeterMode_dotsAscii[] = {
   /*00*/" ", /*01*/".", /*02*/":",
   /*10*/".", /*11*/".", /*12*/":",
   /*20*/":", /*21*/":", /*22*/":"
};

global variable: GraphMeterMode_dotsUtf8

static const char* const GraphMeterMode_dotsUtf8[] = {
   /*00*/" ", /*01*/"⢀", /*02*/"⢠", /*03*/"⢰", /*04*/ "⢸",
   /*10*/"⡀", /*11*/"⣀", /*12*/"⣠", /*13*/"⣰", /*14*/ "⣸",
   /*20*/"⡄", /*21*/"⣄", /*22*/"⣤", /*23*/"⣴", /*24*/ "⣼",
   /*30*/"⡆", /*31*/"⣆", /*32*/"⣦", /*33*/"⣶", /*34*/ "⣾",
   /*40*/"⡇", /*41*/"⣇", /*42*/"⣧", /*43*/"⣷", /*44*/ "⣿"
};

global variable: LEDMeterMode_digits

static const char* const* LEDMeterMode_digits;

global variable: LEDMeterMode_digitsAscii

static const char* const LEDMeterMode_digitsAscii[] = {
   " __ ", "    ", " __ ", " __ ", "    ", " __ ", " __ ", " __ ", " __ ", " __ ",
   "|  |", "   |", " __|", " __|", "|__|", "|__ ", "|__ ", "   |", "|__|", "|__|",
   "|__|", "   |", "|__ ", " __|", "   |", " __|", "|__|", "   |", "|__|", " __|"
};

global variable: LEDMeterMode_digitsUtf8

static const char* const LEDMeterMode_digitsUtf8[] = {
   "┌──┐", "  ┐ ", "╶──┐", "╶──┐", "╷  ╷", "┌──╴", "┌──╴", "╶──┐", "┌──┐", "┌──┐",
   "│  │", "  │ ", "┌──┘", " ──┤", "└──┤", "└──┐", "├──┐", "   │", "├──┤", "└──┤",
   "└──┘", "  ╵ ", "└──╴", "╶──┘", "   ╵", "╶──┘", "└──┘", "   ╵", "└──┘", "╶──┘"
};

global variable: Meter_class

const MeterClass Meter_class = {
   .super = {
      .extends = Class(Object)
   }
};

global variable: Meter_modes

static const MeterMode Meter_modes[] = {
   [0] = {
      .uiName = NULL,
      .h = 0,
      .draw = NULL,
   },
   [BAR_METERMODE] = {
      .uiName = "Bar",
      .h = 1,
      .draw = BarMeterMode_draw,
   },
   [TEXT_METERMODE] = {
      .uiName = "Text",
      .h = 1,
      .draw = TextMeterMode_draw,
   },
   [GRAPH_METERMODE] = {
      .uiName = "Graph",
      .h = GRAPH_HEIGHT,
      .draw = GraphMeterMode_draw,
   },
   [LED_METERMODE] = {
      .uiName = "LED",
      .h = 3,
      .draw = LEDMeterMode_draw,
   },
};

macro: GRAPH_HEIGHT

#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */

macro: PIXPERROW_ASCII

#define PIXPERROW_ASCII 2

macro: PIXPERROW_UTF8

#define PIXPERROW_UTF8 4

macro: UINT32_WIDTH

#define UINT32_WIDTH 32

struct: MeterMode_

typedef struct MeterMode_ {
   Meter_Draw draw;
   const char* uiName;
   int h;
} MeterMode;

Meter.h

Meter.h

enum: MeterRateStatus

typedef enum {
   RATESTATUS_DATA,
   RATESTATUS_INIT,
   RATESTATUS_NODATA,
   RATESTATUS_STALE
} MeterRateStatus;

extern: BlankMeter_class

extern const MeterClass BlankMeter_class;

extern: Meter_class

extern const MeterClass Meter_class;

function: Meter_delete

void Meter_delete(Object* cast);

function: Meter_humanUnit

int Meter_humanUnit(char* buffer, double value, size_t size);

function: Meter_new

Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type);

function: Meter_nextSupportedMode

MeterModeId Meter_nextSupportedMode(const Meter* this);

function: Meter_setCaption

void Meter_setCaption(Meter* this, const char* caption);

function: Meter_setMode

void Meter_setMode(Meter* this, MeterModeId modeIndex);

function: Meter_toListItem

ListItem* Meter_toListItem(const Meter* this, bool moving);

macro: As_Meter

#define As_Meter(this_)                ((const MeterClass*)((this_)->super.klass))

macro: MAX_METER_GRAPHDATA_VALUES

#define MAX_METER_GRAPHDATA_VALUES 32768

macro: Meter_attributes

#define Meter_attributes(this_)        As_Meter(this_)->attributes

macro: METER_BUFFER_APPEND_CHR

#define METER_BUFFER_APPEND_CHR(buffer, size, c)           \
   do {                                                    \
      if ((size) < 2) {                                    \
         return;                                           \
      }                                                    \
      *(buffer)++ = c;                                     \
      *(buffer) = '\0';                                    \
      (size)--;                                            \
      if ((size) == 0) {                                   \
         return;                                           \
      }                                                    \
   } while (0)

macro: METER_BUFFER_CHECK

#define METER_BUFFER_CHECK(buffer, size, written)          \
   do {                                                    \
      if ((written) < 0 || (size_t)(written) >= (size)) {  \
         return;                                           \
      }                                                    \
      (buffer) += (written);                               \
      (size) -= (size_t)(written);                         \
   } while (0)

macro: Meter_done

#define Meter_done(this_)              As_Meter(this_)->done((Meter*)(this_))

macro: Meter_doneFn

#define Meter_doneFn(this_)            As_Meter(this_)->done

macro: Meter_drawFn

#define Meter_drawFn(this_)            As_Meter(this_)->draw

macro: Meter_getCaption

#define Meter_getCaption(this_)        (Meter_getCaptionFn(this_) ? As_Meter(this_)->getCaption((const Meter*)(this_)) : (this_)->caption)

macro: Meter_getCaptionFn

#define Meter_getCaptionFn(this_)      As_Meter(this_)->getCaption

macro: Meter_getUiName

#define Meter_getUiName(this_,n_,l_)   As_Meter(this_)->getUiName((const Meter*)(this_),n_,l_)

macro: Meter_getUiNameFn

#define Meter_getUiNameFn(this_)       As_Meter(this_)->getUiName

macro: Meter_init

#define Meter_init(this_)              As_Meter(this_)->init((Meter*)(this_))

macro: Meter_initFn

#define Meter_initFn(this_)            As_Meter(this_)->init

macro: Meter_isMultiColumn

#define Meter_isMultiColumn(this_)     As_Meter(this_)->isMultiColumn

macro: Meter_name

#define Meter_name(this_)              As_Meter(this_)->name

macro: Meter_supportedModes

#define Meter_supportedModes(this_)    As_Meter(this_)->supportedModes

macro: METER_TXTBUFFER_LEN

#define METER_TXTBUFFER_LEN 256

macro: Meter_uiName

#define Meter_uiName(this_)            As_Meter(this_)->uiName

macro: Meter_updateMode

#define Meter_updateMode(this_, m_)    As_Meter(this_)->updateMode((Meter*)(this_), m_)

macro: Meter_updateModeFn

#define Meter_updateModeFn(this_)      As_Meter(this_)->updateMode

macro: Meter_updateValues

#define Meter_updateValues(this_)      As_Meter(this_)->updateValues((Meter*)(this_))

struct: GraphData_

typedef struct GraphData_ {
   struct timeval time;
   size_t nValues;
   double* values;
} GraphData;

struct: Meter_

struct Meter_;

struct: Meter_

struct Meter_ {
   Object super;
   Meter_Draw draw;
   const Machine* host;

   char* caption;
   MeterModeId mode;
   unsigned int param;
   GraphData drawData;
   int h;
   int columnWidthCount;      /**< only used internally by the Header */
   uint8_t curItems;
   const int* curAttributes;
   char txtBuffer[METER_TXTBUFFER_LEN];
   double* values;
   double total;
   void* meterData;
};

struct: MeterClass_

typedef struct MeterClass_ {
   const ObjectClass super;
   const Meter_Init init;
   const Meter_Done done;
   const Meter_UpdateMode updateMode;
   const Meter_UpdateValues updateValues;
   const Meter_Draw draw;
   const Meter_GetCaption getCaption;
   const Meter_GetUiName getUiName;
   const MeterModeId defaultMode;
   const uint32_t supportedModes;          /* bitset of supported modes, 1<<mode_id */
   const double total;
   const int* const attributes;
   const char* const name;                 /* internal name of the meter, must not contain any space */
   const char* const uiName;               /* display name in header setup menu */
   const char* const caption;              /* prefix in the actual header */
   const char* const description;          /* optional meter description in header setup menu */
   const uint8_t maxItems;
   const bool isMultiColumn;               /* whether the meter draws multiple sub-columns (defaults to false) */
} MeterClass;

typedef: Meter

typedef struct Meter_ Meter;

typedef: Meter_Done

typedef ATTR_NONNULL void (*Meter_Done)(Meter*);

typedef: Meter_Draw

typedef ATTR_NONNULL void (*Meter_Draw)(Meter*, int, int, int);

typedef: Meter_GetCaption

typedef ATTR_NONNULL const char* (*Meter_GetCaption)(const Meter*);

typedef: Meter_GetUiName

typedef ATTR_NONNULL ATTR_ACCESS3_W(2, 3) void (*Meter_GetUiName)(const Meter*, char*, size_t);

typedef: Meter_Init

typedef ATTR_NONNULL void (*Meter_Init)(Meter*);

typedef: Meter_UpdateMode

typedef ATTR_NONNULL void (*Meter_UpdateMode)(Meter*, MeterModeId);

typedef: Meter_UpdateValues

typedef ATTR_NONNULL void (*Meter_UpdateValues)(Meter*);

MeterMode.h

MeterMode.h

enum: MeterModeId_

enum MeterModeId_ {
   /* Meter mode 0 is reserved */
   BAR_METERMODE = 1,
   TEXT_METERMODE,
   GRAPH_METERMODE,
   LED_METERMODE,
   LAST_METERMODE
};

macro: METERMODE_DEFAULT_SUPPORTED

#define METERMODE_DEFAULT_SUPPORTED ( \
   (1 << BAR_METERMODE) |             \
   (1 << TEXT_METERMODE) |            \
   (1 << GRAPH_METERMODE) |           \
   (1 << LED_METERMODE) |             \
   0) // Avoids edits when updating

typedef: MeterModeId

typedef unsigned int MeterModeId;

MetersPanel.c

MetersPanel.c

function: MetersPanel_cleanup

void MetersPanel_cleanup(void) {
   if (Meters_movingBar) {
      FunctionBar_delete(Meters_movingBar);
      Meters_movingBar = NULL;
   }
}

function: MetersPanel_delete

static void MetersPanel_delete(Object* object) {
   MetersPanel* this = (MetersPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: MetersPanel_eventHandler

function: MetersPanel_new

MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr) {
   MetersPanel* this = AllocThis(MetersPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(MetersFunctions, MetersKeys, MetersEvents);
   if (!Meters_movingBar) {
      Meters_movingBar = FunctionBar_new(MetersMovingFunctions, MetersMovingKeys, MetersMovingEvents);
   }
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   this->settings = settings;
   this->meters = meters;
   this->scr = scr;
   this->moving = false;
   this->rightNeighbor = NULL;
   this->leftNeighbor = NULL;
   Panel_setHeader(super, header);
   for (int i = 0; i < Vector_size(meters); i++) {
      const Meter* meter = (const Meter*) Vector_get(meters, i);
      Panel_add(super, (Object*) Meter_toListItem(meter, false));
   }
   return this;
}

function: MetersPanel_setMoving

void MetersPanel_setMoving(MetersPanel* this, bool moving) {
   Panel* super = &this->super;
   this->moving = moving;
   ListItem* selected = (ListItem*)Panel_getSelected(super);
   if (selected) {
      selected->moving = moving;
   }
   if (!moving) {
      Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
      Panel_setDefaultBar(super);
   } else {
      Panel_setSelectionColor(super, PANEL_SELECTION_FOLLOW);
      super->currentBar = Meters_movingBar;
   }
}

function: moveToNeighbor

static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
   Panel* super = &this->super;
   if (this->moving) {
      if (neighbor) {
         if (selected < Vector_size(this->meters)) {
            MetersPanel_setMoving(this, false);

            Meter* meter = (Meter*) Vector_take(this->meters, selected);
            Panel_remove(super, selected);
            Vector_insert(neighbor->meters, selected, meter);
            Panel_insert(&(neighbor->super), selected, (Object*) Meter_toListItem(meter, false));
            Panel_setSelected(&(neighbor->super), selected);

            MetersPanel_setMoving(neighbor, true);
            return true;
         }
      }
   }
   return false;
}

global_variable: Meters_movingBar

static FunctionBar* Meters_movingBar = NULL;

global_variable: MetersEvents

static const int MetersEvents[] = {' ', 13, ERR, KEY_DC, KEY_F(10)};

global_variable: MetersFunctions

static const char* const MetersFunctions[] = {"Style ", "Move  ", "                                       ", "Delete", "Done  ", NULL};

global_variable: MetersKeys

static const char* const MetersKeys[] = {"Space", "Enter", "  ", "Del", "F10"};

global_variable: MetersMovingEvents

static const int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};

global_variable: MetersMovingFunctions

static const char* const MetersMovingFunctions[] = {"Style ", "Lock  ", "Up    ", "Down  ", "Left  ", "Right ", "       ", "Delete", "Done  ", NULL};

global_variable: MetersMovingKeys

static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-", "->", "  ", "Del", "F10"};

struct_instance: MetersPanel_class

const PanelClass MetersPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = MetersPanel_delete
   },
   .eventHandler = MetersPanel_eventHandler
};

MetersPanel.h

MetersPanel.h

extern const: MetersPanel_class

extern const PanelClass MetersPanel_class;

function: MetersPanel_cleanup

void MetersPanel_cleanup(void);

function: MetersPanel_new

MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);

method: MetersPanel_setMoving

void MetersPanel_setMoving(MetersPanel* this, bool moving);

struct: MetersPanel_

struct MetersPanel_ {
   Panel super;

   Settings* settings;
   Vector* meters;
   ScreenManager* scr;
   MetersPanel* leftNeighbor;
   MetersPanel* rightNeighbor;
   bool moving;
};

typedef: MetersPanel

struct MetersPanel_;
typedef struct MetersPanel_ MetersPanel;

NetBSDMachine.c

netbsd/NetBSDMachine.c

file: NetBSDMachine.c

NetBSDMachine.h

netbsd/NetBSDMachine.h

struct: CPUData

typedef struct CPUData_ {
   unsigned long long int totalTime;
   unsigned long long int userTime;
   unsigned long long int niceTime;
   unsigned long long int sysTime;
   unsigned long long int sysAllTime;
   unsigned long long int spinTime;
   unsigned long long int intrTime;
   unsigned long long int idleTime;

   unsigned long long int totalPeriod;
   unsigned long long int userPeriod;
   unsigned long long int nicePeriod;
   unsigned long long int sysPeriod;
   unsigned long long int sysAllPeriod;
   unsigned long long int spinPeriod;
   unsigned long long int intrPeriod;
   unsigned long long int idlePeriod;

   double frequency;
} CPUData;

struct: NetBSDMachine

typedef struct NetBSDMachine_ {
   Machine super;
   kvm_t* kd;

   long fscale;
   int pageSize;
   int pageSizeKB;

   CPUData* cpuData;
} NetBSDMachine;

NetBSDProcess.c

netbsd/NetBSDProcess.c

function: NetBSDProcess_compareByKey

static int NetBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const NetBSDProcess* p1 = (const NetBSDProcess*)v1;
   const NetBSDProcess* p2 = (const NetBSDProcess*)v2;

   // remove if actually used
   (void)p1; (void)p2;

   switch (key) {
   // add NetBSD-specific fields here
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

function: NetBSDProcess_new

Process* NetBSDProcess_new(const Machine* host) {
   NetBSDProcess* this = xCalloc(1, sizeof(NetBSDProcess));
   Object_setClass(this, Class(NetBSDProcess));
   Process_init(&this->super, host);
   return (Process*)this;
}

function: NetBSDProcess_rowWriteField

static void NetBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const NetBSDProcess* np = (const NetBSDProcess*) super;

   char buffer[256]; buffer[255] = '\0';
   int attr = CRT_colors[DEFAULT_COLOR];
   //size_t n = sizeof(buffer) - 1;

   switch (field) {
   // add NetBSD-specific fields here
   default:
      Process_writeField(&np->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

function: Process_delete

void Process_delete(Object* cast) {
   NetBSDProcess* this = (NetBSDProcess*) cast;
   Process_done((Process*)cast);
   free(this);
}

global variable: NetBSDProcess_class

const ProcessClass NetBSDProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = NetBSDProcess_rowWriteField
   },
   .compareByKey = NetBSDProcess_compareByKey
};

global variable: Process_fields

NetBSDProcess.h

netbsd/NetBSDProcess.h

function: NetBSDProcess_new

Process* NetBSDProcess_new(const Machine* host);

function: Process_delete

void Process_delete(Object* cast);

global_variable_declaration: NetBSDProcess_class

extern const ProcessClass NetBSDProcess_class;

global_variable_declaration: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

struct: NetBSDProcess

typedef struct NetBSDProcess_ {
   Process super;
} NetBSDProcess;

NetBSDProcessTable.c

netbsd/NetBSDProcessTable.c

file: NetBSDProcessTable.c

NetBSDProcessTable.h

netbsd/NetBSDProcessTable.h

struct: NetBSDProcessTable_

typedef struct NetBSDProcessTable_ {
   ProcessTable super;
} NetBSDProcessTable;

Platform.c

netbsd/Platform.c

file: Platform.c

Platform.h

netbsd/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

ProcessField.h

netbsd/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   // End of list

README.md

netbsd/README.md

build option: --disable-unicode

`--disable-unicode`

build option: --with-curses=curses

`--with-curses=curses`

function: kvm_getprocs

The full source code for this entity is not provided in the 'netbsd/README.md' file, which is a descriptive document.

function: sysctl

The full source code for this entity is not provided in the 'netbsd/README.md' file, which is a descriptive document.

library: ncurses

The full source code for this entity is not provided in the 'netbsd/README.md' file, which is a descriptive document.

library: NetBSD's curses

The full source code for this entity is not provided in the 'netbsd/README.md' file, which is a descriptive document.

struct: cchar_t

The full source code for this entity is not provided in the 'netbsd/README.md' file, which is a descriptive document.

NetworkIOMeter.c

NetworkIOMeter.c

const MeterClass instance: NetworkIOMeter_class

const MeterClass NetworkIOMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = NetworkIOMeter_display
   },
   .updateValues = NetworkIOMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 2,
   .total = 100.0,
   .attributes = NetworkIOMeter_attributes,
   .name = "NetworkIO",
   .uiName = "Network IO",
   .caption = "Network: "
};

function: NetworkIOMeter_display

static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
   switch (status) {
      case RATESTATUS_NODATA:
         RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
         return;
      case RATESTATUS_INIT:
         RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
         return;
      case RATESTATUS_STALE:
         RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
         return;
      case RATESTATUS_DATA:
         break;
   }

   char buffer[64];

   RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: ");
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_rxb_diff_str);
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");

   RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: ");
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_txb_diff_str);
   RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");

   int len = xSnprintf(buffer, sizeof(buffer), " (%u/%u pkts/s) ", cached_rxp_diff, cached_txp_diff);
   RichString_appendnAscii(out, CRT_colors[METER_TEXT], buffer, len);
}

function: NetworkIOMeter_updateValues

static const int array: NetworkIOMeter_attributes

static const int NetworkIOMeter_attributes[] = {
   METER_VALUE_IOREAD,
   METER_VALUE_IOWRITE,
};

static variable: cached_rxb_diff

static double cached_rxb_diff;

static variable: cached_rxb_diff_str

static char cached_rxb_diff_str[6];

static variable: cached_rxp_diff

static uint32_t cached_rxp_diff;

static variable: cached_txb_diff

static double cached_txb_diff;

static variable: cached_txb_diff_str

static char cached_txb_diff_str[6];

static variable: cached_txp_diff

static uint32_t cached_txp_diff;

static variable: status

static MeterRateStatus status = RATESTATUS_INIT;

NetworkIOMeter.h

NetworkIOMeter.h

struct: NetworkIOData

typedef struct NetworkIOData_ {
   uint64_t bytesReceived;
   uint64_t packetsReceived;
   uint64_t bytesTransmitted;
   uint64_t packetsTransmitted;
} NetworkIOData;

variable: NetworkIOMeter_class

extern const MeterClass NetworkIOMeter_class;

NEWS

NEWS

file: NEWS

See the commit history for news of the past.
See the bug tracker for news of the future.
Run the program for news of the present.

Object.c

Object.c

function: Object_isA

bool Object_isA(const Object* o, const ObjectClass* klass) {
   if (!o)
      return false;

   for (const ObjectClass* type = o->klass; type; type = type->extends) {
      if (type == klass) {
         return true;
      }
   }

   return false;
}

global variable: Object_class

const ObjectClass Object_class = {
   .extends = NULL
};

Object.h

Object.h

function: Object_isA

bool Object_isA(const Object* o, const ObjectClass* klass);

global constant: Object_class

extern const ObjectClass Object_class;

macro: AllocThis

#define AllocThis(class_) (class_*)   xMalloc(sizeof(class_)); Object_setClass(this, Class(class_))

macro: Class

#define Class(class_)                 ((const ObjectClass*)(&(class_ ## _class)))

macro: Object_compare

#define Object_compare(obj_, other_)  (assert(Object_getClass(obj_)->compare), Object_getClass(obj_)->compare((const void*)(obj_), other_))

macro: Object_delete

#define Object_delete(obj_)           (assert(Object_getClass(obj_)->delete), Object_getClass(obj_)->delete((Object*)(obj_)))

macro: Object_display

#define Object_display(obj_, str_)    (assert(Object_getClass(obj_)->display), Object_getClass(obj_)->display((const Object*)(obj_), str_))

macro: Object_displayFn

#define Object_displayFn(obj_)        Object_getClass(obj_)->display

macro: Object_getClass

#define Object_getClass(obj_)         ((const Object*)(obj_))->klass

macro: Object_setClass

#define Object_setClass(obj_, class_) (((Object*)(obj_))->klass = (const ObjectClass*) (class_))

struct: struct Object_

struct Object_ {
   const ObjectClass* klass;
};

typedef: Object_Compare

typedef int(*Object_Compare)(const void*, const void*);

typedef: Object_Delete

typedef void(*Object_Delete)(Object*);

typedef: Object_Display

typedef void(*Object_Display)(const Object*, RichString*);

typedef struct: Object

struct Object_;
typedef struct Object_ Object;

typedef struct: ObjectClass

typedef struct ObjectClass_ {
   const void* const extends;
   const Object_Display display;
   const Object_Delete delete;
   const Object_Compare compare;
} ObjectClass;

typedef union: Arg

typedef union {
   int i;
   void* v;
} Arg;

OpenBSDMachine.c

openbsd/OpenBSDMachine.c

function: getKernelCPUTimes

static void getKernelCPUTimes(unsigned int cpuId, u_int64_t* times) {
   const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId };
   size_t length = sizeof(*times) * CPUSTATES;
   if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) {
      CRT_fatalError("sysctl kern.cp_time2 failed");
   }
}

function: kernelCPUTimesToHtop

static void kernelCPUTimesToHtop(const u_int64_t* times, CPUData* cpu) {
   unsigned long long totalTime = 0;
   for (int i = 0; i < CPUSTATES; i++) {
      totalTime += times[i];
   }

   unsigned long long sysAllTime = times[CP_INTR] + times[CP_SYS];

   // XXX Not sure if CP_SPIN should be added to sysAllTime.
   // See https://github.com/openbsd/src/commit/531d8034253fb82282f0f353c086e9ad827e031c
   #ifdef CP_SPIN
   sysAllTime += times[CP_SPIN];
   #endif

   cpu->totalPeriod = saturatingSub(totalTime, cpu->totalTime);
   cpu->userPeriod = saturatingSub(times[CP_USER], cpu->userTime);
   cpu->nicePeriod = saturatingSub(times[CP_NICE], cpu->niceTime);
   cpu->sysPeriod = saturatingSub(times[CP_SYS], cpu->sysTime);
   cpu->sysAllPeriod = saturatingSub(sysAllTime, cpu->sysAllTime);
   #ifdef CP_SPIN
   cpu->spinPeriod = saturatingSub(times[CP_SPIN], cpu->spinTime);
   #endif
   cpu->intrPeriod = saturatingSub(times[CP_INTR], cpu->intrTime);
   cpu->idlePeriod = saturatingSub(times[CP_IDLE], cpu->idleTime);

   cpu->totalTime = totalTime;
   cpu->userTime = times[CP_USER];
   cpu->niceTime = times[CP_NICE];
   cpu->sysTime = times[CP_SYS];
   cpu->sysAllTime = sysAllTime;
   #ifdef CP_SPIN
   cpu->spinTime = times[CP_SPIN];
   #endif
   cpu->intrTime = times[CP_INTR];
   cpu->idleTime = times[CP_IDLE];
}

function: Machine_delete

void Machine_delete(Machine* super) {
   OpenBSDMachine* this = (OpenBSDMachine*) super;

   if (this->kd) {
      kvm_close(this->kd);
   }
   free(this->cpuData);
   Machine_done(super);
   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* super, unsigned int id) {
   assert(id < super->existingCPUs);

   const OpenBSDMachine* this = (const OpenBSDMachine*) super;
   return this->cpuData[id + 1].online;
}

function: Machine_new

Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
   const int fmib[] = { CTL_KERN, KERN_FSCALE };
   size_t size;
   char errbuf[_POSIX2_LINE_MAX];

   OpenBSDMachine* this = xCalloc(1, sizeof(OpenBSDMachine));
   Machine* super = &this->super;

   Machine_init(super, usersTable, userId);

   OpenBSDMachine_updateCPUcount(this);

   size = sizeof(this->fscale);
   if (sysctl(fmib, 2, &this->fscale, &size, NULL, 0) < 0 || this->fscale <= 0) {
      CRT_fatalError("fscale sysctl call failed");
   }

   if ((this->pageSize = sysconf(_SC_PAGESIZE)) == -1)
      CRT_fatalError("pagesize sysconf call failed");
   this->pageSizeKB = this->pageSize / ONE_K;

   this->kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
   if (this->kd == NULL) {
      CRT_fatalError("kvm_openfiles() failed");
   }

   this->cpuSpeed = -1;

   return super;
}

function: Machine_scan

void Machine_scan(Machine* super) {
   OpenBSDMachine* this = (OpenBSDMachine*) super;

   OpenBSDMachine_updateCPUcount(this);
   OpenBSDMachine_scanMemoryInfo(this);
   OpenBSDMachine_scanCPUTime(this);
}

function: OpenBSDMachine_scanCPUTime

static void OpenBSDMachine_scanCPUTime(OpenBSDMachine* this) {
   Machine* super = &this->super;
   u_int64_t kernelTimes[CPUSTATES] = {0};
   u_int64_t avg[CPUSTATES] = {0};

   for (unsigned int i = 0; i < super->existingCPUs; i++) {
      CPUData* cpu = &this->cpuData[i + 1];

      if (!cpu->online) {
         continue;
      }

      getKernelCPUTimes(i, kernelTimes);
      kernelCPUTimesToHtop(kernelTimes, cpu);

      avg[CP_USER] += cpu->userTime;
      avg[CP_NICE] += cpu->niceTime;
      avg[CP_SYS] += cpu->sysTime;
      #ifdef CP_SPIN
      avg[CP_SPIN] += cpu->spinTime;
      #endif
      avg[CP_INTR] += cpu->intrTime;
      avg[CP_IDLE] += cpu->idleTime;
   }

   for (int i = 0; i < CPUSTATES; i++) {
      avg[i] /= super->activeCPUs;
   }

   kernelCPUTimesToHtop(avg, &this->cpuData[0]);

   {
      const int mib[] = { CTL_HW, HW_CPUSPEED };
      int cpuSpeed;
      size_t size = sizeof(cpuSpeed);
      if (sysctl(mib, 2, &cpuSpeed, &size, NULL, 0) == -1) {
         this->cpuSpeed = -1;
      } else {
         this->cpuSpeed = cpuSpeed;
      }
   }
}

function: OpenBSDMachine_scanMemoryInfo

static void OpenBSDMachine_scanMemoryInfo(OpenBSDMachine* this) {
   Machine* super = &this->super;
   const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP };
   struct uvmexp uvmexp;
   size_t size_uvmexp = sizeof(uvmexp);

   if (sysctl(uvmexp_mib, 2, &uvmexp, &size_uvmexp, NULL, 0) < 0) {
      CRT_fatalError("uvmexp sysctl call failed");
   }

   super->totalMem = uvmexp.npages * this->pageSizeKB;
   super->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * this->pageSizeKB;

   // Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
   const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
   struct bcachestats bcstats;
   size_t size_bcstats = sizeof(bcstats);

   if (sysctl(bcache_mib, 3, &bcstats, &size_bcstats, NULL, 0) < 0) {
      CRT_fatalError("cannot get vfs.bcachestat");
   }

   super->cachedMem = bcstats.numbufpages * this->pageSizeKB;

   /*
    * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
    * All rights reserved.
    *
    * Taken almost directly from OpenBSD's top(1)
    *
    * Originally released under a BSD-3 license
    * Modified through htop developers applying GPL-2
    */
   int nswap = swapctl(SWAP_NSWAP, 0, 0);
   if (nswap > 0) {
      struct swapent* swdev = xMallocArray(nswap, sizeof(struct swapent));
      int rnswap = swapctl(SWAP_STATS, swdev, nswap);

      /* Total things up */
      unsigned long long int total = 0, used = 0;
      for (int i = 0; i < rnswap; i++) {
         if (swdev[i].se_flags & SWF_ENABLE) {
            used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
            total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
         }
      }

      super->totalSwap = total;
      super->usedSwap = used;

      free(swdev);
   } else {
      super->totalSwap = super->usedSwap = 0;
   }
}

function: OpenBSDMachine_updateCPUcount

static void OpenBSDMachine_updateCPUcount(OpenBSDMachine* this) {
   Machine* super = &this->super;
   const int nmib[] = { CTL_HW, HW_NCPU };
   const int mib[] = { CTL_HW, HW_NCPUONLINE };
   int r;
   unsigned int value;
   size_t size;
   bool change = false;

   size = sizeof(value);
   r = sysctl(mib, 2, &value, &size, NULL, 0);
   if (r < 0 || value < 1) {
      value = 1;
   }

   if (value != super->activeCPUs) {
      super->activeCPUs = value;
      change = true;
   }

   size = sizeof(value);
   r = sysctl(nmib, 2, &value, &size, NULL, 0);
   if (r < 0 || value < 1) {
      value = super->activeCPUs;
   }

   if (value != super->existingCPUs) {
      this->cpuData = xReallocArray(this->cpuData, value + 1, sizeof(CPUData));
      super->existingCPUs = value;
      change = true;
   }

   if (change) {
      CPUData* dAvg = &this->cpuData[0];
      memset(dAvg, '\0', sizeof(CPUData));
      dAvg->totalTime = 1;
      dAvg->totalPeriod = 1;
      dAvg->online = true;

      for (unsigned int i = 0; i < super->existingCPUs; i++) {
         CPUData* d = &this->cpuData[i + 1];
         memset(d, '\0', sizeof(CPUData));
         d->totalTime = 1;
         d->totalPeriod = 1;

         const int ncmib[] = { CTL_KERN, KERN_CPUSTATS, i };
         struct cpustats cpu_stats;

         size = sizeof(cpu_stats);
         if (sysctl(ncmib, 3, &cpu_stats, &size, NULL, 0) < 0) {
            CRT_fatalError("ncmib sysctl call failed");
         }
         d->online = (cpu_stats.cs_flags & CPUSTATS_ONLINE);
      }
   }
}

OpenBSDMachine.h

openbsd/OpenBSDMachine.h

struct: CPUData_

typedef struct CPUData_ {
   unsigned long long int totalTime;
   unsigned long long int userTime;
   unsigned long long int niceTime;
   unsigned long long int sysTime;
   unsigned long long int sysAllTime;
   unsigned long long int spinTime;
   unsigned long long int intrTime;
   unsigned long long int idleTime;

   unsigned long long int totalPeriod;
   unsigned long long int userPeriod;
   unsigned long long int nicePeriod;
   unsigned long long int sysPeriod;
   unsigned long long int sysAllPeriod;
   unsigned long long int spinPeriod;
   unsigned long long int intrPeriod;
   unsigned long long int idlePeriod;

   bool online;
} CPUData;

struct: OpenBSDMachine_

typedef struct OpenBSDMachine_ {
   Machine super;
   kvm_t* kd;

   CPUData* cpuData;

   long fscale;
   int cpuSpeed;
   int pageSize;
   int pageSizeKB;

} OpenBSDMachine;

OpenBSDProcess.c

openbsd/OpenBSDProcess.c

function: OpenBSDProcess_new

Process* OpenBSDProcess_new(const Machine* host) {
   OpenBSDProcess* this = xCalloc(1, sizeof(OpenBSDProcess));
   Object_setClass(this, Class(OpenBSDProcess));
   Process_init(&this->super, host);
   return (Process*)this;
}

function: Process_delete

void Process_delete(Object* cast) {
   OpenBSDProcess* this = (OpenBSDProcess*) cast;
   Process_done((Process*)cast);
   free(this);
}

global variable: OpenBSDProcess_class

const ProcessClass OpenBSDProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = OpenBSDProcess_rowWriteField
   },
   .compareByKey = OpenBSDProcess_compareByKey
};

global variable: Process_fields

static function: OpenBSDProcess_compareByKey

static int OpenBSDProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const OpenBSDProcess* p1 = (const OpenBSDProcess*)v1;
   const OpenBSDProcess* p2 = (const OpenBSDProcess*)v2;

   // remove if actually used
   (void)p1; (void)p2;

   switch (key) {
   // add OpenBSD-specific fields here
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

static function: OpenBSDProcess_rowWriteField

static void OpenBSDProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const OpenBSDProcess* op = (const OpenBSDProcess*) super;

   char buffer[256]; buffer[255] = '\0';
   int attr = CRT_colors[DEFAULT_COLOR];
   //size_t n = sizeof(buffer) - 1;

   switch (field) {
   // add OpenBSD-specific fields here
   default:
      Process_writeField(&op->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

OpenBSDProcess.h

openbsd/OpenBSDProcess.h

function: OpenBSDProcess_new

Process* OpenBSDProcess_new(const Machine* host);

function: Process_delete

void Process_delete(Object* cast);

global variable: OpenBSDProcess_class

extern const ProcessClass OpenBSDProcess_class;

global variable: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

struct: OpenBSDProcess

typedef struct OpenBSDProcess_ {
   Process super;

   /* 'Kernel virtual addr of u-area' to detect main threads */
   uint64_t addr;
} OpenBSDProcess;

OpenBSDProcessTable.c

openbsd/OpenBSDProcessTable.c

file: OpenBSDProcessTable.c

OpenBSDProcessTable.h

openbsd/OpenBSDProcessTable.h

struct: OpenBSDProcessTable

typedef struct OpenBSDProcessTable_ {
   ProcessTable super;
} OpenBSDProcessTable;

Platform.c

openbsd/Platform.c

array: Platform_defaultScreens

const ScreenDefaults Platform_defaultScreens[] = {
   {
      .name = "Main",
      .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
      .sortKey = "PERCENT_CPU",
   },
};

array: Platform_meterTypes

const MeterClass* const Platform_meterTypes[] = {
   &CPUMeter_class,
   &ClockMeter_class,
   &DateMeter_class,
   &DateTimeMeter_class,
   &LoadAverageMeter_class,
   &LoadMeter_class,
   &MemoryMeter_class,
   &SwapMeter_class,
   &MemorySwapMeter_class,
   &TasksMeter_class,
   &UptimeMeter_class,
   &BatteryMeter_class,
   &HostnameMeter_class,
   &SysArchMeter_class,
   &AllCPUsMeter_class,
   &AllCPUs2Meter_class,
   &AllCPUs4Meter_class,
   &AllCPUs8Meter_class,
   &LeftCPUsMeter_class,
   &RightCPUsMeter_class,
   &LeftCPUs2Meter_class,
   &RightCPUs2Meter_class,
   &LeftCPUs4Meter_class,
   &RightCPUs4Meter_class,
   &LeftCPUs8Meter_class,
   &RightCPUs8Meter_class,
   &FileDescriptorMeter_class,
   &BlankMeter_class,
   NULL
};

array: Platform_signals

const SignalItem Platform_signals[] = {
   { .name = " 0 Cancel",    .number =  0 },
   { .name = " 1 SIGHUP",    .number =  1 },
   { .name = " 2 SIGINT",    .number =  2 },
   { .name = " 3 SIGQUIT",   .number =  3 },
   { .name = " 4 SIGILL",    .number =  4 },
   { .name = " 5 SIGTRAP",   .number =  5 },
   { .name = " 6 SIGABRT",   .number =  6 },
   { .name = " 6 SIGIOT",    .number =  6 },
   { .name = " 7 SIGEMT",    .number =  7 },
   { .name = " 8 SIGFPE",    .number =  8 },
   { .name = " 9 SIGKILL",   .number =  9 },
   { .name = "10 SIGBUS",    .number = 10 },
   { .name = "11 SIGSEGV",   .number = 11 },
   { .name = "12 SIGSYS",    .number = 12 },
   { .name = "13 SIGPIPE",   .number = 13 },
   { .name = "14 SIGALRM",   .number = 14 },
   { .name = "15 SIGTERM",   .number = 15 },
   { .name = "16 SIGURG",    .number = 16 },
   { .name = "17 SIGSTOP",   .number = 17 },
   { .name = "18 SIGTSTP",   .number = 18 },
   { .name = "19 SIGCONT",   .number = 19 },
   { .name = "20 SIGCHLD",   .number = 20 },
   { .name = "21 SIGTTIN",   .number = 21 },
   { .name = "22 SIGTTOU",   .number = 22 },
   { .name = "23 SIGIO",     .number = 23 },
   { .name = "24 SIGXCPU",   .number = 24 },
   { .name = "25 SIGXFSZ",   .number = 25 },
   { .name = "26 SIGVTALRM", .number = 26 },
   { .name = "27 SIGPROF",   .number = 27 },
   { .name = "28 SIGWINCH",  .number = 28 },
   { .name = "29 SIGINFO",   .number = 29 },
   { .name = "30 SIGUSR1",   .number = 30 },
   { .name = "31 SIGUSR2",   .number = 31 },
   { .name = "32 SIGTHR",    .number = 32 },
};

function: findDevice

static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
   for (int devn = 0;; devn++) {
      mib[2] = devn;
      if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
         if (errno == ENXIO)
            continue;
         if (errno == ENOENT)
            return false;
      }
      if (String_eq(name, snsrdev->xname)) {
         return true;
      }
   }
}

function: Platform_done

void Platform_done(void) {
   /* no platform-specific cleanup needed */
}

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC) {
   int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
   struct sensor s;
   size_t slen = sizeof(struct sensor);
   struct sensordev snsrdev;
   size_t sdlen = sizeof(struct sensordev);

   bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);

   *percent = NAN;
   if (found) {
      /* last full capacity */
      mib[3] = 7;
      mib[4] = 0;
      double last_full_capacity = 0;
      if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1)
         last_full_capacity = s.value;
      if (last_full_capacity > 0) {
         /*  remaining capacity */
         mib[3] = 7;
         mib[4] = 3;
         if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
            double charge = s.value;
            *percent = 100 * (charge / last_full_capacity);
            if (charge >= last_full_capacity) {
               *percent = 100;
            }
         }
      }
   }

   found = findDevice("acpiac0", mib, &snsrdev, &sdlen);

   *isOnAC = AC_ERROR;
   if (found) {
      mib[3] = 9;
      mib[4] = 0;
      if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1)
         *isOnAC = s.value;
   }
}

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data) {
   // TODO
   (void)data;
   return false;
}

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max) {
   static const int mib_kern_maxfile[] = { CTL_KERN, KERN_MAXFILES };
   int sysctl_maxfile = 0;
   size_t size_maxfile = sizeof(int);
   if (sysctl(mib_kern_maxfile, ARRAYSIZE(mib_kern_maxfile), &sysctl_maxfile, &size_maxfile, NULL, 0) < 0) {
      *max = NAN;
   } else if (size_maxfile != sizeof(int) || sysctl_maxfile < 1) {
      *max = NAN;
   } else {
      *max = sysctl_maxfile;
   }

   static const int mib_kern_nfiles[] = { CTL_KERN, KERN_NFILES };
   int sysctl_nfiles = 0;
   size_t size_nfiles = sizeof(int);
   if (sysctl(mib_kern_nfiles, ARRAYSIZE(mib_kern_nfiles), &sysctl_nfiles, &size_nfiles, NULL, 0) < 0) {
      *used = NAN;
   } else if (size_nfiles != sizeof(int) || sysctl_nfiles < 0) {
      *used = NAN;
   } else {
      *used = sysctl_nfiles;
   }
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
   struct loadavg loadAverage;
   const int mib[2] = { CTL_VM, VM_LOADAVG };
   size_t size = sizeof(loadAverage);

   int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
   if (err) {
      *one = 0;
      *five = 0;
      *fifteen = 0;
      return;
   }
   *one     = (double) loadAverage.ldavg[0] / loadAverage.fscale;
   *five    = (double) loadAverage.ldavg[1] / loadAverage.fscale;
   *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void) {
   return 2 * THREAD_PID_OFFSET;
}

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data) {
   // TODO
   (void)data;
   return false;
}

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid) {
   char errbuf[_POSIX2_LINE_MAX];
   char* env;
   char** ptr;
   int count;
   kvm_t* kt;
   struct kinfo_proc* kproc;
   size_t capacity = 4096, size = 0;

   if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) {
      return NULL;
   }

   if ((kproc = kvm_getprocs(kt, KERN_PROC_PID, pid,
                             sizeof(struct kinfo_proc), &count)) == NULL) {
      (void) kvm_close(kt);
      return NULL;
   }

   if ((ptr = kvm_getenvv(kt, kproc, 0)) == NULL) {
      (void) kvm_close(kt);
      return NULL;
   }

   env = xMalloc(capacity);
   for (char** p = ptr; *p; p++) {
      size_t len = strlen(*p) + 1;

      while (size + len > capacity) {
         if (capacity > (SIZE_MAX / 2)) {
            free(env);
            env = NULL;
            goto end;
         }

         capacity *= 2;
         env = xRealloc(env, capacity);
      }

      strlcpy(env + size, *p, len);
      size += len;
   }

   if (size < 2 || env[size - 1] || env[size - 2]) {
      if (size + 2 < capacity)
         env = xRealloc(env, capacity + 2);
      env[size] = 0;
      env[size + 1] = 0;
   }

end:
   (void) kvm_close(kt);
   return env;
}

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
   (void)pid;
   return NULL;
}

function: Platform_getUptime

int Platform_getUptime(void) {
   struct timeval bootTime, currTime;
   const int mib[2] = { CTL_KERN, KERN_BOOTTIME };
   size_t size = sizeof(bootTime);

   int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
   if (err) {
      return -1;
   }
   gettimeofday(&currTime, NULL);

   return (int) difftime(currTime.tv_sec, bootTime.tv_sec);
}

function: Platform_init

bool Platform_init(void) {
   /* no platform-specific setup needed */
   return true;
}

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys) {
   /* no platform-specific key bindings */
   (void) keys;
}

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu) {
   const Machine* host = this->host;
   const OpenBSDMachine* ohost = (const OpenBSDMachine*) host;
   const CPUData* cpuData = &ohost->cpuData[cpu];
   double total;
   double totalPercent;
   double* v = this->values;

   if (!cpuData->online) {
      this->curItems = 0;
      return NAN;
   }

   total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod;

   v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
   v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
   if (host->settings->detailedCPUTime) {
      v[CPU_METER_KERNEL]  = cpuData->sysPeriod / total * 100.0;
      v[CPU_METER_IRQ]     = cpuData->intrPeriod / total * 100.0;
      v[CPU_METER_SOFTIRQ] = 0.0;
      v[CPU_METER_STEAL]   = 0.0;
      v[CPU_METER_GUEST]   = 0.0;
      v[CPU_METER_IOWAIT]  = 0.0;
      v[CPU_METER_FREQUENCY] = NAN;
      this->curItems = 8;
   } else {
      v[CPU_METER_KERNEL] = cpuData->sysAllPeriod / total * 100.0;
      v[CPU_METER_IRQ] = 0.0; // No steal nor guest on OpenBSD
      this->curItems = 4;
   }
   totalPercent = v[CPU_METER_NICE] + v[CPU_METER_NORMAL] + v[CPU_METER_KERNEL] + v[CPU_METER_IRQ];

   totalPercent = CLAMP(totalPercent, 0.0, 100.0);

   v[CPU_METER_TEMPERATURE] = NAN;

   v[CPU_METER_FREQUENCY] = (ohost->cpuSpeed != -1) ? ohost->cpuSpeed : NAN;

   return totalPercent;
}

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this) {
   const Machine* host = this->host;
   long int usedMem = host->usedMem;
   long int buffersMem = host->buffersMem;
   long int cachedMem = host->cachedMem;
   usedMem -= buffersMem + cachedMem;
   this->total = host->totalMem;
   this->values[MEMORY_METER_USED] = usedMem;
   // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm"
   // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux"
   this->values[MEMORY_METER_BUFFERS] = buffersMem;
   this->values[MEMORY_METER_CACHE] = cachedMem;
   // this->values[MEMORY_METER_AVAILABLE] = "available memory"
}

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this) {
   const Machine* host = this->host;
   this->total = host->totalSwap;
   this->values[SWAP_METER_USED] = host->usedSwap;
   // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux"
   // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux"
}

unsigned int: Platform_numberOfDefaultScreens

const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);

unsigned int: Platform_numberOfSignals

const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);

Platform.h

openbsd/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

variable: Platform_signals

extern const SignalItem Platform_signals[];

ProcessField.h

openbsd/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   // End of list

OpenFilesScreen.c

OpenFilesScreen.c

file: OpenFilesScreen.c

OpenFilesScreen.h

OpenFilesScreen.h

extern variable: OpenFilesScreen_class

extern const InfoScreenClass OpenFilesScreen_class;

function: OpenFilesScreen_delete

void OpenFilesScreen_delete(Object* this);

function: OpenFilesScreen_new

OpenFilesScreen* OpenFilesScreen_new(const Process* process);

struct: OpenFilesScreen_

typedef struct OpenFilesScreen_ {
   InfoScreen super;
   pid_t pid;
} OpenFilesScreen;

OptionItem.c

OptionItem.c

constant: CheckItem_class

const OptionItemClass CheckItem_class = {
   .super = {
      .extends = Class(OptionItem),
      .delete = OptionItem_delete,
      .display = CheckItem_display
   },
   .kind = OPTION_ITEM_CHECK
};

constant: NumberItem_class

const OptionItemClass NumberItem_class = {
   .super = {
      .extends = Class(OptionItem),
      .delete = OptionItem_delete,
      .display = NumberItem_display
   },
   .kind = OPTION_ITEM_NUMBER
};

constant: OptionItem_class

const OptionItemClass OptionItem_class = {
   .super = {
      .extends = Class(Object),
      .delete = OptionItem_delete
   }
};

constant: TextItem_class

const OptionItemClass TextItem_class = {
   .super = {
      .extends = Class(OptionItem),
      .delete = OptionItem_delete,
      .display = TextItem_display
   },
   .kind = OPTION_ITEM_TEXT
};

function: CheckItem_display

static void CheckItem_display(const Object* cast, RichString* out) {
   const CheckItem* this = (const CheckItem*)cast;
   assert (this != NULL);

   RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
   if (CheckItem_get(this)) {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
   } else {
      RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
   }
   RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]    ");
   RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
}

function: CheckItem_get

bool CheckItem_get(const CheckItem* this) {
   if (this->ref) {
      return *(this->ref);
   } else {
      return this->value;
   }
}

function: CheckItem_newByRef

CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
   CheckItem* this = AllocThis(CheckItem);
   this->super.text = xStrdup(text);
   this->value = false;
   this->ref = ref;
   return this;
}

function: CheckItem_newByVal

CheckItem* CheckItem_newByVal(const char* text, bool value) {
   CheckItem* this = AllocThis(CheckItem);
   this->super.text = xStrdup(text);
   this->value = value;
   this->ref = NULL;
   return this;
}

function: CheckItem_set

void CheckItem_set(CheckItem* this, bool value) {
   if (this->ref) {
      *(this->ref) = value;
   } else {
      this->value = value;
   }
}

function: CheckItem_toggle

void CheckItem_toggle(CheckItem* this) {
   if (this->ref) {
      *(this->ref) = !*(this->ref);
   } else {
      this->value = !this->value;
   }
}

function: NumberItem_decrease

void NumberItem_decrease(NumberItem* this) {
   if (this->ref) {
      *(this->ref) = CLAMP(*(this->ref) - 1, this->min, this->max);
   } else {
      this->value = CLAMP(this->value - 1, this->min, this->max);
   }
}

function: NumberItem_display

static void NumberItem_display(const Object* cast, RichString* out) {
   const NumberItem* this = (const NumberItem*)cast;
   assert (this != NULL);

   char buffer[12];
   RichString_writeAscii(out, CRT_colors[CHECK_BOX], "[");
   int written;
   if (this->scale < 0) {
      written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
   } else if (this->scale > 0) {
      written = xSnprintf(buffer, sizeof(buffer), "%d", (int) (pow(10, this->scale) * NumberItem_get(this)));
   } else {
      written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
   }
   RichString_appendnAscii(out, CRT_colors[CHECK_MARK], buffer, written);
   RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
   for (int i = written; i < 5; i++) {
      RichString_appendAscii(out, CRT_colors[CHECK_BOX], " ");
   }
   RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->super.text);
}

function: NumberItem_get

int NumberItem_get(const NumberItem* this) {
   if (this->ref) {
      return *(this->ref);
   } else {
      return this->value;
   }
}

function: NumberItem_increase

void NumberItem_increase(NumberItem* this) {
   if (this->ref) {
      *(this->ref) = CLAMP(*(this->ref) + 1, this->min, this->max);
   } else {
      this->value = CLAMP(this->value + 1, this->min, this->max);
   }
}

function: NumberItem_newByRef

NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max) {
   assert(min <= max);

   NumberItem* this = AllocThis(NumberItem);
   this->super.text = xStrdup(text);
   this->value = 0;
   this->ref = ref;
   this->scale = scale;
   this->min = min;
   this->max = max;
   return this;
}

function: NumberItem_newByVal

NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max) {
   assert(min <= max);

   NumberItem* this = AllocThis(NumberItem);
   this->super.text = xStrdup(text);
   this->value = CLAMP(value, min, max);
   this->ref = NULL;
   this->scale = scale;
   this->min = min;
   this->max = max;
   return this;
}

function: NumberItem_toggle

void NumberItem_toggle(NumberItem* this) {
   if (this->ref) {
      if (*(this->ref) >= this->max)
         *(this->ref) = this->min;
      else
         *(this->ref) += 1;
   } else {
      if (this->value >= this->max)
         this->value = this->min;
      else
         this->value += 1;
   }
}

function: OptionItem_delete

static void OptionItem_delete(Object* cast) {
   OptionItem* this = (OptionItem*)cast;
   assert (this != NULL);

   free(this->text);
   free(this);
}

function: TextItem_display

static void TextItem_display(const Object* cast, RichString* out) {
   const TextItem* this = (const TextItem*)cast;
   assert (this != NULL);

   RichString_appendWide(out, CRT_colors[HELP_BOLD], this->super.text);
}

function: TextItem_new

TextItem* TextItem_new(const char* text) {
   TextItem* this = AllocThis(TextItem);
   this->super.text = xStrdup(text);
   return this;
}

OptionItem.h

OptionItem.h

enum: OptionItemType

enum OptionItemType {
   OPTION_ITEM_TEXT,
   OPTION_ITEM_CHECK,
   OPTION_ITEM_NUMBER,
};

function: CheckItem_get

bool CheckItem_get(const CheckItem* this);

function: CheckItem_newByRef

CheckItem* CheckItem_newByRef(const char* text, bool* ref);

function: CheckItem_newByVal

CheckItem* CheckItem_newByVal(const char* text, bool value);

function: CheckItem_set

void CheckItem_set(CheckItem* this, bool value);

function: CheckItem_toggle

void CheckItem_toggle(CheckItem* this);

function: NumberItem_decrease

void NumberItem_decrease(NumberItem* this);

function: NumberItem_get

int NumberItem_get(const NumberItem* this);

function: NumberItem_increase

void NumberItem_increase(NumberItem* this);

function: NumberItem_newByRef

NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max);

function: NumberItem_newByVal

NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max);

function: NumberItem_toggle

void NumberItem_toggle(NumberItem* this);

function: TextItem_new

TextItem* TextItem_new(const char* text);

global object: CheckItem_class

extern const OptionItemClass CheckItem_class;

global object: NumberItem_class

extern const OptionItemClass NumberItem_class;

global object: OptionItem_class

extern const OptionItemClass OptionItem_class;

global object: TextItem_class

extern const OptionItemClass TextItem_class;

macro: As_OptionItem

#define As_OptionItem(this_)                ((const OptionItemClass*)((this_)->super.klass))

macro: OptionItem_kind

#define OptionItem_kind(this_)              As_OptionItem(this_)->kind

struct: CheckItem

typedef struct CheckItem_ {
   OptionItem super;

   bool* ref;
   bool value;
} CheckItem;

struct: CheckItem_

typedef struct CheckItem_ {
   OptionItem super;

   bool* ref;
   bool value;
} CheckItem;

struct: NumberItem

typedef struct NumberItem_ {
   OptionItem super;

   char* text;
   int* ref;
   int value;
   int scale;
   int min;
   int max;
} NumberItem;

struct: NumberItem_

typedef struct NumberItem_ {
   OptionItem super;

   char* text;
   int* ref;
   int value;
   int scale;
   int min;
   int max;
} NumberItem;

struct: OptionItem

typedef struct OptionItem_ {
   Object super;

   char* text;
} OptionItem;

struct: OptionItem_

typedef struct OptionItem_ {
   Object super;

   char* text;
} OptionItem;

struct: OptionItemClass

typedef struct OptionItemClass_ {
   const ObjectClass super;

   enum OptionItemType kind;
} OptionItemClass;

struct: OptionItemClass_

typedef struct OptionItemClass_ {
   const ObjectClass super;

   enum OptionItemType kind;
} OptionItemClass;

struct: TextItem

typedef struct TextItem_ {
   OptionItem super;

   char* text;
} TextItem;

struct: TextItem_

typedef struct TextItem_ {
   OptionItem super;

   char* text;
} TextItem;

Panel.c

Panel.c

file: Panel.c

Panel.h

Panel.h

enum: HandlerResult_

typedef enum HandlerResult_ {
   HANDLED     = 0x01,
   IGNORED     = 0x02,
   BREAK_LOOP  = 0x04,
   REFRESH     = 0x08,
   REDRAW      = 0x10,
   RESCAN      = 0x20,
   RESIZE      = 0x40,
   SYNTH_KEY   = 0x80,
} HandlerResult;

function: Panel_add

void Panel_add(Panel* this, Object* o);

function: Panel_delete

void Panel_delete(Object* cast);

function: Panel_done

void Panel_done(Panel* this);

function: Panel_draw

void Panel_draw(Panel* this, bool force_redraw, bool focus, bool highlightSelected, bool hideFunctionBar);

function: Panel_get

Object* Panel_get(Panel* this, int i);

function: Panel_getCh

int Panel_getCh(Panel* this);

function: Panel_getSelected

Object* Panel_getSelected(Panel* this);

function: Panel_getSelectedIndex

int Panel_getSelectedIndex(const Panel* this);

function: Panel_init

void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);

function: Panel_insert

void Panel_insert(Panel* this, int i, Object* o);

function: Panel_move

void Panel_move(Panel* this, int x, int y);

function: Panel_moveSelectedDown

void Panel_moveSelectedDown(Panel* this);

function: Panel_moveSelectedUp

void Panel_moveSelectedUp(Panel* this);

function: Panel_new

Panel* Panel_new(int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);

function: Panel_onKey

bool Panel_onKey(Panel* this, int key);

function: Panel_prune

void Panel_prune(Panel* this);

function: Panel_remove

Object* Panel_remove(Panel* this, int i);

function: Panel_resize

void Panel_resize(Panel* this, int w, int h);

function: Panel_selectByTyping

HandlerResult Panel_selectByTyping(Panel* this, int ch);

function: Panel_set

void Panel_set(Panel* this, int i, Object* o);

function: Panel_setCursorToSelection

void Panel_setCursorToSelection(Panel* this);

function: Panel_setHeader

void Panel_setHeader(Panel* this, const char* header);

function: Panel_setSelected

void Panel_setSelected(Panel* this, int selected);

function: Panel_setSelectionColor

void Panel_setSelectionColor(Panel* this, ColorElements colorId);

function: Panel_size

int Panel_size(const Panel* this);

function: Panel_splice

void Panel_splice(Panel* this, Vector* from);

macro: As_Panel

#define As_Panel(this_)                        ((const PanelClass*)((this_)->super.klass))

macro: EVENT_HEADER_CLICK

#define EVENT_HEADER_CLICK(x_) (-10000 + (x_))

macro: EVENT_HEADER_CLICK_GET_X

#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)

macro: EVENT_IS_HEADER_CLICK

#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)

macro: EVENT_IS_SCREEN_TAB_CLICK

#define EVENT_IS_SCREEN_TAB_CLICK(ev_) ((ev_) >= -20000 && (ev_) < -10000)

macro: EVENT_SCREEN_TAB_CLICK

#define EVENT_SCREEN_TAB_CLICK(x_) (-20000 + (x_))

macro: EVENT_SCREEN_TAB_GET_X

#define EVENT_SCREEN_TAB_GET_X(ev_) ((ev_) + 20000)

macro: EVENT_SET_SELECTED

#define EVENT_SET_SELECTED (-1)

macro: KEY_CTRL

#define KEY_CTRL(l) ((l)-'A'+1)

macro: Panel_drawFunctionBar

#define Panel_drawFunctionBar(this_, hideFB_)  (assert(As_Panel(this_)->drawFunctionBar), As_Panel(this_)->drawFunctionBar((Panel*)(this_), hideFB_))

macro: Panel_drawFunctionBarFn

#define Panel_drawFunctionBarFn(this_)         As_Panel(this_)->drawFunctionBar

macro: Panel_eventHandler

#define Panel_eventHandler(this_, ev_)         (assert(As_Panel(this_)->eventHandler), As_Panel(this_)->eventHandler((Panel*)(this_), ev_))

macro: Panel_eventHandlerFn

#define Panel_eventHandlerFn(this_)            As_Panel(this_)->eventHandler

macro: Panel_printHeader

#define Panel_printHeader(this_)               (assert(As_Panel(this_)->printHeader), As_Panel(this_)->printHeader((Panel*)(this_)))

macro: Panel_printHeaderFn

#define Panel_printHeaderFn(this_)             As_Panel(this_)->printHeader

macro: Panel_setDefaultBar

#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)

struct: Panel_

struct Panel_ {
   Object super;
   int x, y, w, h;
   int cursorX, cursorY;
   Vector* items;
   int selected;
   int oldSelected;
   int selectedLen;
   void* eventHandlerState;
   int scrollV;
   int scrollH;
   bool needsRedraw;
   bool cursorOn;
   bool wasFocus;
   FunctionBar* currentBar;
   FunctionBar* defaultBar;
   RichString header;
   ColorElements selectionColorId;
};

struct: Panel_

struct Panel_;

struct: PanelClass_

typedef struct PanelClass_ {
   const ObjectClass super;
   const Panel_EventHandler eventHandler;
   const Panel_DrawFunctionBar drawFunctionBar;
   const Panel_PrintHeader printHeader;
} PanelClass;

typedef: Panel

typedef struct Panel_ Panel;

typedef: Panel_DrawFunctionBar

typedef void (*Panel_DrawFunctionBar)(Panel*, bool);

typedef: Panel_EventHandler

typedef HandlerResult (*Panel_EventHandler)(Panel*, int);

typedef: Panel_PrintHeader

typedef void (*Panel_PrintHeader)(Panel*);

variable: Panel_class

extern const PanelClass Panel_class;

pcp-htop.5.in

pcp-htop.5.in

file: pcp-htop.5.in

pcp-htop.c

pcp-htop.c

function: main

int main(int argc, char** argv) {
   pmSetProgname(program);

   /* extract environment variables */
   opts.flags |= PM_OPTFLAG_ENV_ONLY;
   (void)pmGetOptions(argc, argv, &opts);

   return CommandLine_run(argc, argv);
}

variable: program

const char* program = "pcp-htop";

container

pcp/columns/container

file: container

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[container]
heading = Container
caption = CONTAINER
width = -12
metric = proc.id.container
description = Name of processes container via cgroup heuristics

delayacct

pcp/columns/delayacct

file: delayacct

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[blkio]
heading = BLKIOD
caption = BLKIO_TIME
width = 6
metric = proc.psinfo.delayacct_blkio_time
description = Aggregated block I/O delays

fdcount

pcp/columns/fdcount

file: fdcount

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[fds]
heading = FDS
caption = FDCOUNT
width = 4
metric = proc.fd.count
description = Open file descriptors

gpu_memory

pcp/columns/gpu_memory

file: gpu_memory

guest

pcp/columns/guest

file: guest

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[guest]
heading = GUEST
caption = GUEST_TIME
width = 6
metric = proc.psinfo.guest_time
description = Guest time for the process

[cguest]
heading = CGUEST
caption = CGUEST_TIME
width = 6
metric = proc.psinfo.guest_time + proc.psinfo.cguest_time
description = Cumulative guest time for the process and its children

memory

pcp/columns/memory

file: memory

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[vmdata]
heading = VDATA
width = 6
metric = proc.memory.vmdata
description = Virtual memory used for data

[vmstack]
heading = VSTACK
width = -6
metric = proc.memory.vmstack
description = Virtual memory used for stack

[vmexe]
heading = VEXEC
width = 6
metric = proc.memory.vmexe
description = Virtual memory used for non-library executable code

[vmlib]
heading = VLIBS
width = 6
metric = proc.memory.vmlib
description = Virtual memory used for libraries

[vmswap]
heading = VSWAP
width = 6
metric = proc.memory.vmswap
description = Virtual memory size currently swapped out

[vmlock]
heading = VLOCK
width = 6
metric = proc.memory.vmlock
description = Locked virtual memory

sched

pcp/columns/sched

file: sched

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[rundelay]
heading = RUNQ
caption = RUN_DELAY
width = 4
metric = proc.schedstat.run_delay
description = Run queue time

swap

pcp/columns/swap

file: swap

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[swap]
heading = SWAP
width = 5
metric = proc.psinfo.nswap
description = Count of swap operations for the process

[cswap]
heading = CSWAP
width = 5
metric = proc.psinfo.nswap + proc.psinfo.cnswap
description = Cumulative swap operations for the process and its children

tcp

pcp/columns/tcp

file: tcp

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[tcp_send_packets]
heading = TCPS
caption = TCP_SEND
width = 6
metric = bpf.proc.net.tcp.send.packets
description = Count of TCP packets sent

[tcp_send_bytes]
heading = TCPSB
caption = TCP_SEND_BYTES
width = 6
metric = bpf.proc.net.tcp.send.bytes
description = Cumulative bytes sent via TCP

[tcp_recv_packets]
heading = TCPR
caption = TCP_RECV
width = 6
metric = bpf.proc.net.tcp.recv.packets
description = Count of TCP packets received

[tcp_recv_bytes]
heading = TCPRB
caption = TCP_RECV_BYTES
width = 6
metric = bpf.proc.net.tcp.recv.bytes
description = Cumulative bytes received via TCP

udp

pcp/columns/udp

file: udp

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[udp_send_packets]
heading = UDPS
caption = UDP_SEND
width = 6
metric = bpf.proc.net.udp.send.packets
description = Count of UDP packets sent

[udp_send_bytes]
heading = UDPSB
caption = UDP_SEND_BYTES
width = 6
metric = bpf.proc.net.udp.send.bytes
description = Cumulative bytes sent via UDP

[udp_recv_packets]
heading = UDPR
caption = UDP_RECV
width = 6
metric = bpf.proc.net.udp.recv.packets
description = Count of UDP packets received

[udp_recv_bytes]
heading = UDPRB
caption = UDP_RECV_BYTES
width = 6
metric = bpf.proc.net.udp.recv.bytes
description = Cumulative bytes received via UDP

wchan

pcp/columns/wchan

file: wchan

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[wchan]
heading = WCHAN
caption = WCHAN_ADDRESS
width = 8
metric = proc.psinfo.wchan
description = Wait channel, kernel address process is blocked or sleeping on

[wchans]
heading = WCHANS
caption = WCHAN_SYMBOL
width = -12
metric = proc.psinfo.wchan_s
description = Wait channel, kernel symbol process is blocked or sleeping on

InDomTable.c

pcp/InDomTable.c

function: InDomTable_delete

static void InDomTable_delete(Object* cast) {
   InDomTable* this = (InDomTable*) cast;
   InDomTable_done(this);
   free(this);
}

function: InDomTable_done

void InDomTable_done(InDomTable* this) {
   Table_done(&this->super);
}

function: InDomTable_getInstance

static Instance* InDomTable_getInstance(InDomTable* this, int id, bool* preExisting) {
   const Table* super = &this->super;
   Instance* inst = (Instance*) Hashtable_get(super->table, id);
   *preExisting = inst != NULL;
   if (inst) {
      assert(Vector_indexOf(super->rows, inst, Row_idEqualCompare) != -1);
      assert(Instance_getId(inst) == id);
   } else {
      inst = Instance_new(super->host, this);
      assert(inst->name == NULL);
      Instance_setId(inst, id);
   }
   return inst;
}

function: InDomTable_goThroughEntries

static void InDomTable_goThroughEntries(InDomTable* this) {
   Table* super = &this->super;

   /* for every instance ... */
   int instid = -1, offset = -1;
   while (Metric_iterate(this->metricKey, &instid, &offset)) {
      bool preExisting;
      Instance* inst = InDomTable_getInstance(this, instid, &preExisting);
      inst->offset = offset >= 0 ? offset : 0;

      Row* row = (Row*) inst;
      if (!preExisting)
         Table_add(super, row);
      row->updated = true;
      row->show = true;
   }
}

function: InDomTable_iterateEntries

static void InDomTable_iterateEntries(Table* super) {
   InDomTable* this = (InDomTable*) super;
   InDomTable_goThroughEntries(this);
}

function: InDomTable_new

InDomTable* InDomTable_new(Machine* host, pmInDom indom, int metricKey) {
   InDomTable* this = xCalloc(1, sizeof(InDomTable));
   Object_setClass(this, Class(InDomTable));
   this->metricKey = metricKey;
   this->id = indom;

   Table* super = &this->super;
   Table_init(super, Class(Row), host);

   return this;
}

struct: InDomTable_class

const TableClass InDomTable_class = {
   .super = {
      .extends = Class(Table),
      .delete = InDomTable_delete,
   },
   .prepare = Table_prepareEntries,
   .iterate = InDomTable_iterateEntries,
   .cleanup = Table_cleanupEntries,
};

InDomTable.h

pcp/InDomTable.h

function: InDomTable_done

void InDomTable_done(InDomTable* this);

function: InDomTable_new

InDomTable* InDomTable_new(Machine* host, pmInDom indom, int metricKey);

function: InDomTable_scan

void InDomTable_scan(Table* super);

function: RowField_keyAt

RowField RowField_keyAt(const Settings* settings, int at);

struct: InDomTable

typedef struct InDomTable_ {
   Table super;
   pmInDom id;  /* shared by metrics in the table */
   unsigned int metricKey;  /* representative metric using this indom */
} InDomTable;

variable: InDomTable_class

extern const TableClass InDomTable_class;

Instance.c

pcp/Instance.c

function: Instance_compare

static int Instance_compare(const void* v1, const void* v2) {
   const Instance* i1 = (const Instance*)v1;
   const Instance* i2 = (const Instance*)v2;
   const ScreenSettings* ss = i1->super.host->settings->ss;
   RowField key = ScreenSettings_getActiveSortKey(ss);
   int result = Instance_compareByKey(v1, v2, key);

   // Implement tie-breaker (needed to make tree mode more stable)
   if (!result)
      return SPACESHIP_NUMBER(Instance_getId(i1), Instance_getId(i2));

   return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result;
}

function: Instance_compareByKey

static int Instance_compareByKey(const Row* v1, const Row* v2, int key) {
   const Instance* i1 = (const Instance*)v1;
   const Instance* i2 = (const Instance*)v2;

   if (key < 0)
      return 0;

   Hashtable* dc = Platform_dynamicColumns();
   const PCPDynamicColumn* column = Hashtable_get(dc, key);
   if (!column)
      return -1;

   size_t metric = column->id;
   unsigned int type = Metric_type(metric);

   pmAtomValue atom1 = {0}, atom2 = {0};
   if (!Metric_instance(metric, i1->offset, i1->offset, &atom1, type) ||
       !Metric_instance(metric, i2->offset, i2->offset, &atom2, type)) {
      if (type == PM_TYPE_STRING) {
         free(atom1.cp);
         free(atom2.cp);
      }
      return -1;
   }

   switch (type) {
      case PM_TYPE_STRING: {
         int cmp = SPACESHIP_NULLSTR(atom2.cp, atom1.cp);
         free(atom2.cp);
         free(atom1.cp);
         return cmp;
      }
      case PM_TYPE_32:
         return SPACESHIP_NUMBER(atom2.l, atom1.l);
      case PM_TYPE_U32:
         return SPACESHIP_NUMBER(atom2.ul, atom1.ul);
      case PM_TYPE_64:
         return SPACESHIP_NUMBER(atom2.ll, atom1.ll);
      case PM_TYPE_U64:
         return SPACESHIP_NUMBER(atom2.ull, atom1.ull);
      case PM_TYPE_FLOAT:
         return SPACESHIP_NUMBER(atom2.f, atom1.f);
      case PM_TYPE_DOUBLE:
         return SPACESHIP_NUMBER(atom2.d, atom1.d);
      default:
         break;
   }

   return 0;
}

function: Instance_delete

static void Instance_delete(Object* cast) {
   Instance* this = (Instance*) cast;
   Instance_done(this);
   free(this);
}

function: Instance_done

void Instance_done(Instance* this) {
   if (this->name)
      free(this->name);
   Row_done(&this->super);
}

function: Instance_externalName

static const char* Instance_externalName(Row* super) {
   Instance* this = (Instance*) super;

   if (!this->name)
      /* ignore any failure here - its safe and we try again next time */
      (void)pmNameInDom(InDom_getId(this), Instance_getId(this), &this->name);
   return this->name;
}

function: Instance_new

Instance* Instance_new(const Machine* host, const InDomTable* indom) {
   Instance* this = xCalloc(1, sizeof(Instance));
   Object_setClass(this, Class(Instance));

   Row* super = &this->super;
   Row_init(super, host);

   this->indom = indom;

   return this;
}

function: Instance_writeField

static void Instance_writeField(const Row* super, RichString* str, RowField field) {
   const Instance* this = (const Instance*) super;
   int instid = Instance_getId(this);

   const Settings* settings = super->host->settings;
   DynamicColumn* column = Hashtable_get(settings->dynamicColumns, field);
   PCPDynamicColumn* cp = (PCPDynamicColumn*) column;
   if (!cp)
      return;

   pmAtomValue atom;
   pmAtomValue* ap = &atom;
   const pmDesc* descp = Metric_desc(cp->id);
   if (!Metric_instance(cp->id, instid, this->offset, ap, descp->type))
      ap = NULL;

   PCPDynamicColumn_writeAtomValue(cp, str, settings, cp->id, instid, descp, ap);

   if (ap && descp->type == PM_TYPE_STRING)
      free(ap->cp);
}

global constant struct initialization: Instance_class

const RowClass Instance_class = {
   .super = {
      .extends = Class(Row),
      .display = Row_display,
      .delete = Instance_delete,
      .compare = Instance_compare,
   },
   .sortKeyString = Instance_externalName,
   .writeField = Instance_writeField,
};

Instance.h

pcp/Instance.h

extern const variable: Instance_class

extern const RowClass Instance_class;

function: Instance_done

void Instance_done(Instance* this);

function: Instance_new

Instance* Instance_new(const Machine* host, const struct InDomTable_* indom);

macro: InDom_getId

#define InDom_getId(i_)          ((i_)->indom->id)

macro: Instance_getId

#define Instance_getId(i_)       ((i_)->super.id)

macro: Instance_setId

#define Instance_setId(i_, id_)  ((i_)->super.id = (id_))

struct: Instance_

typedef struct Instance_ {
   Row super;

   char* name; /* external instance name */
   const struct InDomTable_* indom;  /* instance domain */

   /* default result offset to use for searching metrics with instances */
   unsigned int offset;
} Instance;

typedef: Instance

typedef struct Instance_ {
   Row super;

   char* name; /* external instance name */
   const struct InDomTable_* indom;  /* instance domain */

   /* default result offset to use for searching metrics with instances */
   unsigned int offset;
} Instance;

entropy

pcp/meters/entropy

file: entropy

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[entropy]
caption = Entropy
description = Entropy pool
avail.metric = kernel.all.entropy.avail / kernel.all.entropy.poolsize * 100
avail.label = avail
avail.suffix = %

freespace

pcp/meters/freespace

file: freespace

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[freespace]
caption = Freespace
description = Filesystem space
used.metric = sum(filesys.used)
used.color = blue
free.metric = sum(filesys.free)
free.color = green

gpu

pcp/meters/gpu

file: gpu

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[amd_gpu_load]
caption = AMD GPU load
description = AMD GPU load
load.metric = amdgpu.gpu.load
load.suffix = %

[amd_gpu_average_power]
caption = AMD GPU average power
description = AMD GPU average power in Watts
average_power.metric = amdgpu.gpu.average_power
average_power.suffix = W

[amd_gpu_memory]
type = bar
caption = AMD GPU memory
description = Allocated frame buffer memory
used.metric = amdgpu.memory.used
used.color = red
total.metric = amdgpu.memory.total
total.color: blue

[amd_gpu_clock]
caption = AMD GPU clock
description = The GPU clock speed in MHz
clock.metric = amdgpu.gpu.clock
clock.label = MHz

[amd_gpu_memory_clock]
caption = AMD GPU memory clock
description = The GDDRx memory clock speed in MHz
clock.metric = amdgpu.memory.clock
clock.label = MHz

[amd_gpu_temp]
caption = AMD GPU temperature
description = The GPU temperature in degrees Celsius
temp.metric = amdgpu.gpu.temperature / 1000
temp.label = °C

ipc

pcp/meters/ipc

file: ipc

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[ipc]
caption = SysV IPC
description = SysV IPC counts
msg.metric = ipc.msg.used_queues
msg.color = blue
sem.metric = ipc.sem.used_sem
sem.color = green
shm.metric = ipc.shm.used_ids
shm.color = cyan

locks

pcp/meters/locks

file: locks

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[locks]
caption = File locks
description = VFS file locks
posix.metric = vfs.locks.posix.count
posix.color = blue
flock.metric = vfs.locks.flock.count
flock.color = green
readlock.metric = vfs.locks.posix.read + vfs.locks.flock.read
readlock.color = red
writelock.metric = vfs.locks.posix.write + vfs.locks.flock.write
writelock.color = yellow

memcache

pcp/meters/memcache

file: memcache

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[memcache]
caption = Memcache
description = Memcache Hits
hit.metric = sum(memcache.hits)
hit.color = green
miss.metric = sum(memcache.misses)
miss.color = blue

mysql

pcp/meters/mysql

file: mysql

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[mysql_io]
caption = MySQL I/O
description = MySQL throughput
recv.metric = mysql.status.bytes_received
recv.color = green
sent.metric = mysql.status.bytes_sent
sent.color = blue

[mysql_keys]
caption = MySQL keys
description = MySQL key status
key_blocks_used.metric = mysql.status.key_blocks_used
key_blocks_used.color = yellow
key_blocks_used.label = used
key_reads.metric = mysql.status.key_reads
key_reads.label = read
key_reads.color = green
key_writes.metric = mysql.status.key_writes
key_writes.label = writ
key_writes.color = blue
key_read_requests.metric = mysql.status.key_read_requests
key_read_requests.label = rreq
key_read_requests.color = green
key_write_requests.metric = mysql.status.key_write_requests
key_write_requests.label = wreq
key_write_requests.color = blue

[innodb_buffer]
caption = InnoDB pool
description = InnoDB buffer pool
created.metric = mysql.status.innodb_pages_created
created.label = cr
created.color = yellow
read.metric = mysql.status.innodb_pages_read
read.label = rd
read.color = green
written.metric = mysql.status.innodb_pages_written
written.label = wr
written.color = red

[innodb_io]
caption = InnoDB I/O
description = InnoDB I/O operations
read.metric = mysql.status.innodb_data_read
read.label = rd
read.color = green
written.metric = mysql.status.innodb_data.writes
written.label = wr
written.color = blue
sync.metric = mysql.status.innodb_data_fsyncs
sync.label = sync
sync.color = cyan

[innodb_ops]
caption = InnoDB ops
description = InnoDB operations
inserted.metric = mysql.status.innodb_rows_inserted
inserted.label = ins
inserted.color = blue
updated.metric = mysql.status.innodb_rows_updated
updated.label = upd
updated.color = cyan
deleted.metric = mysql.status.innodb_rows_deleted
deleted.label = del
deleted.color = red
read.metric = mysql.status.innodb_rows_read
read.label = rd
read.color = green

postfix

pcp/meters/postfix

file: postfix

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[postfix]
caption = Postfix
incoming.metric = sum(postfix.queues.incoming)
incoming.color = green
incoming.label = in
active.metric = sum(postfix.queues.active)
active.color = blue
active.label = act
deferred.metric = sum(postfix.queues.deferred)
deferred.color = cyan
deferred.label = dfr
bounce.metric = sum(postfix.queues.maildrop)
bounce.color = red
bounce.label = bnc
hold.metric = sum(postfix.queues.hold)
hold.color = yellow

redis

pcp/meters/redis

file: redis

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[redisxact]
caption = Redis xact
description = Redis transactions
tps.metric = redis.instantaneous_ops_per_sec
tps.color = green

[redismem]
caption = Redis mem
description = Redis memory
lua.metric = redis.used_memory_lua
lua.color = magenta
used.metric = redis.used_memory
used.color = blue

[redisclient]
caption = Redis clients
description = Redis clients
type = bar
blocked.metric = redis.blocked_clients
blocked.color = blue
blocked.label = blk
clients.metric = redis.connected_clients
clients.color = green
clients.label = conn

[redisconn]
caption = Redis conn
description = Redis connections
type = bar
reject.metric = redis.rejected_connections
reject.color = magenta
reject.label = fail/s
total.metric = redis.total_connections_received
total.color = blue
total.label = conn/s

tcp

pcp/meters/tcp

file: tcp

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[tcp]
caption = TCP
description = TCP sockets
listen.metric = network.tcpconn.listen
listen.color = green
listen.label = lis
active.metric = network.tcpconn.established
active.color = blue
active.label = act
syn.metric = network.tcpconn.syn_sent + network.tcpconn.syn_recv + network.tcpconn.last_ack
syn.color = cyan
wait.metric = network.tcpconn.time_wait
wait.color = red
wait.label = tim
close.metric = network.tcpconn.fin_wait1 + network.tcpconn.fin_wait2 + network.tcpconn.close + network.tcpconn.close_wait + network.tcpconn.closing
close.color = yellow
close.label = clo

Metric.c

pcp/Metric.c

function: Metric_desc

const pmDesc* Metric_desc(Metric metric) {
   return &pcp->descs[metric];
}

function: Metric_enable

void Metric_enable(Metric metric, bool enable) {
   pcp->fetch[metric] = enable ? pcp->pmids[metric] : PM_ID_NULL;
}

function: Metric_enabled

bool Metric_enabled(Metric metric) {
   return pcp->fetch[metric] != PM_ID_NULL;
}

function: Metric_enableThreads

void Metric_enableThreads(void) {
   pmValueSet* vset = xCalloc(1, sizeof(pmValueSet));
   vset->vlist[0].inst = PM_IN_NULL;
   vset->vlist[0].value.lval = 1;
   vset->valfmt = PM_VAL_INSITU;
   vset->numval = 1;
   vset->pmid = pcp->pmids[PCP_CONTROL_THREADS];

   pmResult* result = xCalloc(1, sizeof(pmResult));
   result->vset[0] = vset;
   result->numpmid = 1;

   int sts = pmStore(result);
   if (sts < 0 && pmDebugOptions.appl0)
      fprintf(stderr, "Error: cannot enable threads: %s\n", pmErrStr(sts));

   pmFreeResult(result);
}

function: Metric_externalName

void Metric_externalName(Metric metric, int inst, char** externalName) {
   const pmDesc* desc = &pcp->descs[metric];
   /* ignore a failure here - its safe to do so */
   (void)pmNameInDom(desc->indom, inst, externalName);
}

function: Metric_extract

static pmAtomValue* Metric_extract(Metric metric, int inst, int offset, pmValueSet* vset, pmAtomValue* atom, int type) {

   /* extract value (using requested type) of given metric instance */
   const pmDesc* desc = &pcp->descs[metric];
   const pmValue* value = &vset->vlist[offset];
   int sts = pmExtractValue(vset->valfmt, value, desc->type, atom, type);
   if (sts < 0) {
      if (pmDebugOptions.appl0)
         fprintf(stderr, "Error: cannot extract %s instance %d value: %s\n",
                         pcp->names[metric], inst, pmErrStr(sts));
      memset(atom, 0, sizeof(pmAtomValue));
   }
   return atom;
}

function: Metric_fetch

bool Metric_fetch(struct timeval* timestamp) {
   if (pcp->result) {
      pmFreeResult(pcp->result);
      pcp->result = NULL;
   }
   int sts, count = 0;
   do {
      sts = pmFetch(pcp->totalMetrics, pcp->fetch, &pcp->result);
   } while (sts == PM_ERR_IPC && ++count < 3);
   if (sts < 0) {
      if (pmDebugOptions.appl0)
         fprintf(stderr, "Error: cannot fetch metric values: %s\n",
                 pmErrStr(sts));
      return false;
   }
   if (timestamp)
      *timestamp = pcp->result->timestamp;
   return true;
}

function: Metric_instance

pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue* atom, int type) {

   pmValueSet* vset = pcp->result->vset[metric];
   if (!vset || vset->numval <= 0)
      return NULL;

   /* fast-path using heuristic offset based on expected location */
   if (offset >= 0 && offset < vset->numval && inst == vset->vlist[offset].inst)
      return Metric_extract(metric, inst, offset, vset, atom, type);

   /* slow-path using a linear search for the requested instance */
   for (int i = 0; i < vset->numval; i++) {
      if (inst == vset->vlist[i].inst)
         return Metric_extract(metric, inst, i, vset, atom, type);
   }
   return NULL;
}

function: Metric_instanceCount

int Metric_instanceCount(Metric metric) {
   pmValueSet* vset = pcp->result->vset[metric];
   if (vset)
      return vset->numval;
   return 0;
}

function: Metric_instanceOffset

int Metric_instanceOffset(Metric metric, int inst) {
   pmValueSet* vset = pcp->result->vset[metric];
   if (!vset || vset->numval <= 0)
      return 0;

   /* search for optimal offset for subsequent inst lookups to begin */
   for (int i = 0; i < vset->numval; i++) {
      if (inst == vset->vlist[i].inst)
         return i;
   }
   return 0;
}

function: Metric_iterate

bool Metric_iterate(Metric metric, int* instp, int* offsetp) {
   if (!pcp->result)
      return false;

   pmValueSet* vset = pcp->result->vset[metric];
   if (!vset || vset->numval <= 0)
      return false;

   int offset = *offsetp;
   offset = (offset < 0) ? 0 : offset + 1;
   if (offset > vset->numval - 1)
      return false;

   *offsetp = offset;
   *instp = vset->vlist[offset].inst;
   return true;
}

function: Metric_lookupText

int Metric_lookupText(const char* metric, char** desc) {
   pmID pmid;
   int sts;

   sts = pmLookupName(1, &metric, &pmid);
   if (sts < 0)
      return sts;

   if (pmLookupText(pmid, PM_TEXT_ONELINE, desc) >= 0)
      (*desc)[0] = toupper((*desc)[0]); /* UI consistency */
   return 0;
}

function: Metric_type

int Metric_type(Metric metric) {
   return pcp->descs[metric].type;
}

function: Metric_values

pmAtomValue* Metric_values(Metric metric, pmAtomValue* atom, int count, int type) {
   if (pcp->result == NULL)
      return NULL;

   pmValueSet* vset = pcp->result->vset[metric];
   if (!vset || vset->numval <= 0)
      return NULL;

   /* extract requested number of values as requested type */
   const pmDesc* desc = &pcp->descs[metric];
   for (int i = 0; i < vset->numval; i++) {
      if (i == count)
         break;
      const pmValue* value = &vset->vlist[i];
      int sts = pmExtractValue(vset->valfmt, value, desc->type, &atom[i], type);
      if (sts < 0) {
         if (pmDebugOptions.appl0)
            fprintf(stderr, "Error: cannot extract metric value: %s\n",
                            pmErrStr(sts));
         memset(&atom[i], 0, sizeof(pmAtomValue));
      }
   }
   return atom;
}

Metric.h

pcp/Metric.h

enum: Metric

function: Metric_desc

const pmDesc* Metric_desc(Metric metric);

function: Metric_enable

void Metric_enable(Metric metric, bool enable);

function: Metric_enabled

bool Metric_enabled(Metric metric);

function: Metric_enableThreads

void Metric_enableThreads(void);

function: Metric_externalName

void Metric_externalName(Metric metric, int inst, char** externalName);

function: Metric_fetch

bool Metric_fetch(struct timeval* timestamp);

function: Metric_instance

pmAtomValue* Metric_instance(Metric metric, int inst, int offset, pmAtomValue* atom, int type);

function: Metric_instanceCount

int Metric_instanceCount(Metric metric);

function: Metric_instanceOffset

int Metric_instanceOffset(Metric metric, int inst);

function: Metric_iterate

bool Metric_iterate(Metric metric, int* instp, int* offsetp);

function: Metric_lookupText

int Metric_lookupText(const char* metric, char** desc);

function: Metric_type

int Metric_type(Metric metric);

function: Metric_values

pmAtomValue* Metric_values(Metric metric, pmAtomValue* atom, int count, int type);

PCPDynamicColumn.c

pcp/PCPDynamicColumn.c

function: PCPDynamicColumn_addMetric

static bool PCPDynamicColumn_addMetric(PCPDynamicColumns* columns, PCPDynamicColumn* column) {
   if (!column->super.name[0])
      return false;

   char* metricName = NULL;
   xAsprintf(&metricName, "htop.column.%s", column->super.name);

   column->metricName = metricName;
   column->id = columns->offset + columns->cursor;
   columns->cursor++;

   Platform_addMetric(column->id, metricName);
   return true;
}

function: PCPDynamicColumn_compareByKey

int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key) {
   const Process* proc = &p1->super;
   const Settings* settings = proc->super.host->settings;
   const PCPDynamicColumn* column = Hashtable_get(settings->dynamicColumns, key);

   if (!column)
      return -1;

   size_t metric = column->id;
   unsigned int type = Metric_type(metric);

   pmAtomValue atom1 = {0}, atom2 = {0};
   if (!Metric_instance(metric, Process_getPid(&p1->super), p1->offset, &atom1, type) ||
       !Metric_instance(metric, Process_getPid(&p2->super), p2->offset, &atom2, type)) {
      if (type == PM_TYPE_STRING) {
         free(atom1.cp);
         free(atom2.cp);
      }
      return -1;
   }

   switch (type) {
      case PM_TYPE_STRING: {
         int cmp = SPACESHIP_NULLSTR(atom2.cp, atom1.cp);
         free(atom2.cp);
         free(atom1.cp);
         return cmp;
      }
      case PM_TYPE_32:
         return SPACESHIP_NUMBER(atom2.l, atom1.l);
      case PM_TYPE_U32:
         return SPACESHIP_NUMBER(atom2.ul, atom1.ul);
      case PM_TYPE_64:
         return SPACESHIP_NUMBER(atom2.ll, atom1.ll);
      case PM_TYPE_U64:
         return SPACESHIP_NUMBER(atom2.ull, atom1.ull);
      case PM_TYPE_FLOAT:
         return compareRealNumbers(atom2.f, atom1.f);
      case PM_TYPE_DOUBLE:
         return compareRealNumbers(atom2.d, atom1.d);
      default:
         break;
   }

   return -1;
}

function: PCPDynamicColumn_done

void PCPDynamicColumn_done(PCPDynamicColumn* this) {
   DynamicColumn_done(&this->super);
   free(this->metricName);
   free(this->format);
}

function: PCPDynamicColumn_new

static PCPDynamicColumn* PCPDynamicColumn_new(PCPDynamicColumns* columns, const char* name) {
   PCPDynamicColumn* column = xCalloc(1, sizeof(*column));
   String_safeStrncpy(column->super.name, name, sizeof(column->super.name));
   column->super.enabled = false;
   column->percent = false;
   column->instances = false;
   column->defaultEnabled = true;

   size_t id = columns->count + LAST_PROCESSFIELD;
   Hashtable_put(columns->table, id, column);
   columns->count++;

   return column;
}

function: PCPDynamicColumn_normalize

static int PCPDynamicColumn_normalize(const pmDesc* desc, const pmAtomValue* ap, double* value) {
   /* form normalized units based on the original metric units */
   pmUnits units = desc->units;
   if (units.dimTime)
      units.scaleTime = PM_TIME_SEC;
   if (units.dimSpace)
      units.scaleSpace = PM_SPACE_BYTE;
   if (units.dimCount)
      units.scaleCount = PM_COUNT_ONE;

   pmAtomValue atom;
   int sts, type = desc->type;
   if ((sts = pmConvScale(type, ap, &desc->units, &atom, &units)) < 0)
      return sts;

   switch (type) {
      case PM_TYPE_32:
         *value = (double) atom.l;
         break;
      case PM_TYPE_U32:
         *value = (double) atom.ul;
         break;
      case PM_TYPE_64:
         *value = (double) atom.ll;
         break;
      case PM_TYPE_U64:
         *value = (double) atom.ull;
         break;
      case PM_TYPE_FLOAT:
         *value = (double) atom.f;
         break;
      case PM_TYPE_DOUBLE:
         *value = atom.d;
         break;
      default:
         return PM_ERR_CONV;
   }

   return 0;
}

function: PCPDynamicColumn_parseFile

function: PCPDynamicColumn_parseMetric

static void PCPDynamicColumn_parseMetric(PCPDynamicColumns* columns, PCPDynamicColumn* column, const char* path, unsigned int line, char* value) {
   /* pmLookupText */
   if (!column->super.description)
      Metric_lookupText(value, &column->super.description);

   /* lookup a dynamic metric with this name, else create */
   if (PCPDynamicColumn_addMetric(columns, column) == false)
      return;

   /* derived metrics in all dynamic columns for simplicity */
   char* error;
   if (pmRegisterDerivedMetric(column->metricName, value, &error) < 0) {
      char* note;
      xAsprintf(&note,
                "%s: failed to parse expression in %s at line %u\n%s\n",
                pmGetProgname(), path, line, error);
      free(error);
      errno = EINVAL;
      CRT_fatalError(note);
      free(note);
   }
}

function: PCPDynamicColumn_scanDir

static void PCPDynamicColumn_scanDir(PCPDynamicColumns* columns, char* path) {
   DIR* dir = opendir(path);
   if (!dir)
      return;

   struct dirent* dirent;
   while ((dirent = readdir(dir)) != NULL) {
      if (dirent->d_name[0] == '.')
         continue;

      char* file = String_cat(path, dirent->d_name);
      PCPDynamicColumn_parseFile(columns, file);
      free(file);
   }
   closedir(dir);
}

function: PCPDynamicColumn_setupWidth

static void PCPDynamicColumn_setupWidth(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
   PCPDynamicColumn* column = (PCPDynamicColumn*) value;

   /* calculate column size based on config file and metric units */
   const pmDesc* desc = Metric_desc(column->id);

   if (column->instances || desc->type == PM_TYPE_STRING) {
      column->super.width = column->width;
      if (column->super.width == 0)
         column->super.width = -16;
      return;
   }

   if (column->format) {
      if (strcmp(column->format, "percent") == 0) {
         column->super.width = 5;
         return;
      }
      if (strcmp(column->format, "process") == 0) {
         column->super.width = Process_pidDigits;
         return;
      }
   }

   if (column->width) {
      column->super.width = column->width;
      return;
   }

   pmUnits units = desc->units;
   if (units.dimSpace && units.dimTime)
      column->super.width = 11; // Row_printRate
   else if (units.dimSpace)
      column->super.width = 5;  // Row_printBytes
   else if (units.dimCount && units.dimTime)
      column->super.width = 11;  // Row_printCount
   else if (units.dimTime)
      column->super.width = 8;  // Row_printTime
   else
      column->super.width = 11; // Row_printCount
}

function: PCPDynamicColumn_uniqueName

static bool PCPDynamicColumn_uniqueName(char* key, PCPDynamicColumns* columns) {
   return DynamicColumn_search(columns->table, key, NULL) == NULL;
}

function: PCPDynamicColumn_validateColumnName

static bool PCPDynamicColumn_validateColumnName(char* key, const char* path, unsigned int line) {
   char* p = key;
   char* end = strrchr(key, ']');

   if (end) {
      *end = '\0';
   } else {
      fprintf(stderr,
                "%s: no closing brace on column name at %s line %u\n\"%s\"",
                pmGetProgname(), path, line, key);
      return false;
   }

   while (*p) {
      if (p == key) {
         if (!isalpha(*p) && *p != '_')
            break;
      } else {
         if (!isalnum(*p) && *p != '_')
            break;
      }
      p++;
   }
   if (*p != '\0') { /* badness */
      fprintf(stderr,
                "%s: invalid column name at %s line %u\n\"%s\"",
                pmGetProgname(), path, line, key);
      return false;
   }
   return true;
}

function: PCPDynamicColumn_writeAtomValue

function: PCPDynamicColumn_writeField

void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str) {
   const Settings* settings = proc->super.host->settings;
   const PCPProcess* pp = (const PCPProcess*) proc;
   const pmDesc* desc = Metric_desc(this->id);
   pid_t pid = Process_getPid(proc);

   pmAtomValue atom;
   pmAtomValue* ap = &atom;
   if (!Metric_instance(this->id, pid, pp->offset, ap, desc->type))
      ap = NULL;

   PCPDynamicColumn_writeAtomValue(this, str, settings, this->id, pid, desc, ap);
}

function: PCPDynamicColumns_done

void PCPDynamicColumns_done(Hashtable* table) {
   Hashtable_foreach(table, PCPDynamicColumns_free, NULL);
}

function: PCPDynamicColumns_free

static void PCPDynamicColumns_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
   PCPDynamicColumn* column = (PCPDynamicColumn*) value;
   PCPDynamicColumn_done(column);
}

function: PCPDynamicColumns_init

void PCPDynamicColumns_init(PCPDynamicColumns* columns) {
   const char* share = pmGetConfig("PCP_SHARE_DIR");
   const char* sysconf = pmGetConfig("PCP_SYSCONF_DIR");
   const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
   const char* override = getenv("PCP_HTOP_DIR");
   const char* home = getenv("HOME");
   char* path;

   if (!xdgConfigHome && !home) {
      const struct passwd* pw = getpwuid(getuid());
      if (pw)
         home = pw->pw_dir;
   }

   columns->table = Hashtable_new(0, true);

   /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */
   if (override) {
      path = String_cat(override, "/columns/");
      PCPDynamicColumn_scanDir(columns, path);
      free(path);
   }

   /* next, search in home directory alongside htoprc */
   if (xdgConfigHome)
      path = String_cat(xdgConfigHome, "/htop/columns/");
   else if (home)
      path = String_cat(home, CONFIGDIR "/htop/columns/");
   else
      path = NULL;
   if (path) {
      PCPDynamicColumn_scanDir(columns, path);
      free(path);
   }

   /* next, search in the system columns directory */
   path = String_cat(sysconf, "/htop/columns/");
   PCPDynamicColumn_scanDir(columns, path);
   free(path);

   /* next, try the readonly system columns directory */
   path = String_cat(share, "/htop/columns/");
   PCPDynamicColumn_scanDir(columns, path);
   free(path);
}

function: PCPDynamicColumns_setupWidths

void PCPDynamicColumns_setupWidths(PCPDynamicColumns* columns) {
   Hashtable_foreach(columns->table, PCPDynamicColumn_setupWidth, NULL);
}

PCPDynamicColumn.h

pcp/PCPDynamicColumn.h

function: PCPDynamicColumn_compareByKey

int PCPDynamicColumn_compareByKey(const PCPProcess* p1, const PCPProcess* p2, ProcessField key);

function: PCPDynamicColumn_done

void PCPDynamicColumn_done(PCPDynamicColumn* this);

function: PCPDynamicColumn_writeAtomValue

void PCPDynamicColumn_writeAtomValue(PCPDynamicColumn* column, RichString* str, const struct Settings_* settings, int metric, int instance, const struct pmDesc* desc, const void* atomvalue);

function: PCPDynamicColumn_writeField

void PCPDynamicColumn_writeField(PCPDynamicColumn* this, const Process* proc, RichString* str);

function: PCPDynamicColumns_done

void PCPDynamicColumns_done(Hashtable* table);

function: PCPDynamicColumns_init

void PCPDynamicColumns_init(PCPDynamicColumns* columns);

function: PCPDynamicColumns_setupWidths

void PCPDynamicColumns_setupWidths(PCPDynamicColumns* columns);

struct: PCPDynamicColumn_

typedef struct PCPDynamicColumn_ {
   DynamicColumn super;
   char* metricName;
   char* format;
   size_t id;  /* identifier for metric array lookups */
   int width;  /* optional width from configuration file */
   bool defaultEnabled;  /* default enabled in dynamic screen */
   bool percent;
   bool instances;  /* an instance *names* column, not values */
} PCPDynamicColumn;

struct: PCPDynamicColumns_

typedef struct PCPDynamicColumns_ {
   Hashtable* table;
   size_t count;  /* count of dynamic columns discovered by scan */
   size_t offset; /* start offset into the Platform metric array */
   size_t cursor; /* identifier allocator for each new metric used */
} PCPDynamicColumns;

PCPDynamicMeter.c

pcp/PCPDynamicMeter.c

function: PCPDynamicMeter_display

function: PCPDynamicMeter_enable

void PCPDynamicMeter_enable(PCPDynamicMeter* this) {
   for (size_t i = 0; i < this->totalMetrics; i++)
      Metric_enable(this->metrics[i].id, true);
}

function: PCPDynamicMeter_free

static void PCPDynamicMeter_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
   PCPDynamicMeter* meter = (PCPDynamicMeter*) value;
   for (size_t i = 0; i < meter->totalMetrics; i++) {
      free(meter->metrics[i].name);
      free(meter->metrics[i].label);
      free(meter->metrics[i].suffix);
   }
   free(meter->metrics);
   free(meter->super.caption);
   free(meter->super.description);
}

function: PCPDynamicMeter_lookupMetric

static PCPDynamicMetric* PCPDynamicMeter_lookupMetric(PCPDynamicMeters* meters, PCPDynamicMeter* meter, const char* name) {
   char* metricName = NULL;
   xAsprintf(&metricName, "htop.meter.%s.%s", meter->super.name, name);

   PCPDynamicMetric* metric;
   for (size_t i = 0; i < meter->totalMetrics; i++) {
      metric = &meter->metrics[i];
      if (String_eq(metric->name, metricName)) {
         free(metricName);
         return metric;
      }
   }

   /* not an existing metric in this meter - add it */
   size_t n = meter->totalMetrics + 1;
   meter->metrics = xReallocArray(meter->metrics, n, sizeof(PCPDynamicMetric));
   meter->totalMetrics = n;
   metric = &meter->metrics[n - 1];
   memset(metric, 0, sizeof(PCPDynamicMetric));
   metric->name = metricName;
   metric->label = String_cat(name, ": ");
   metric->id = meters->offset + meters->cursor;
   meters->cursor++;

   Platform_addMetric(metric->id, metricName);

   return metric;
}

function: PCPDynamicMeter_new

static PCPDynamicMeter* PCPDynamicMeter_new(PCPDynamicMeters* meters, const char* name) {
   PCPDynamicMeter* meter = xCalloc(1, sizeof(*meter));
   String_safeStrncpy(meter->super.name, name, sizeof(meter->super.name));
   Hashtable_put(meters->table, ++meters->count, meter);
   return meter;
}

function: PCPDynamicMeter_parseFile

function: PCPDynamicMeter_parseMetric

function: PCPDynamicMeter_scanDir

static void PCPDynamicMeter_scanDir(PCPDynamicMeters* meters, char* path) {
   DIR* dir = opendir(path);
   if (!dir)
      return;

   struct dirent* dirent;
   while ((dirent = readdir(dir)) != NULL) {
      if (dirent->d_name[0] == '.')
         continue;

      char* file = String_cat(path, dirent->d_name);
      PCPDynamicMeter_parseFile(meters, file);
      free(file);
   }
   closedir(dir);
}

function: PCPDynamicMeter_uniqueName

// Ensure a meter name has not been defined previously
static bool PCPDynamicMeter_uniqueName(char* key, PCPDynamicMeters* meters) {
   return !DynamicMeter_search(meters->table, key, NULL);
}

function: PCPDynamicMeter_updateValues

function: PCPDynamicMeter_validateMeterName

// Ensure a valid name for use in a PCP metric name and in htoprc
static bool PCPDynamicMeter_validateMeterName(char* key, const char* path, unsigned int line) {
   char* p = key;
   char* end = strrchr(key, ']');

   if (end) {
      *end = '\0';
   } else {
      fprintf(stderr,
              "%s: no closing brace on meter name at %s line %u\n\"%s\"\n",
              pmGetProgname(), path, line, key);
      return false;
   }

   while (*p) {
      if (p == key) {
         if (!isalpha(*p) && *p != '_')
            break;
      } else {
         if (!isalnum(*p) && *p != '_')
            break;
      }
      p++;
   }
   if (*p != '\0') { /* badness */
      fprintf(stderr,
              "%s: invalid meter name at %s line %u\n\"%s\"\n",
              pmGetProgname(), path, line, key);
      return false;
   }
   return true;
}

function: PCPDynamicMeters_done

void PCPDynamicMeters_done(Hashtable* table) {
   Hashtable_foreach(table, PCPDynamicMeter_free, NULL);
}

function: PCPDynamicMeters_init

void PCPDynamicMeters_init(PCPDynamicMeters* meters) {
   const char* share = pmGetConfig("PCP_SHARE_DIR");
   const char* sysconf = pmGetConfig("PCP_SYSCONF_DIR");
   const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
   const char* override = getenv("PCP_HTOP_DIR");
   const char* home = getenv("HOME");
   char* path;

   if (!xdgConfigHome && !home) {
      const struct passwd* pw = getpwuid(getuid());
      if (pw)
         home = pw->pw_dir;
   }

   meters->table = Hashtable_new(0, true);

   /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */
   if (override) {
      path = String_cat(override, "/meters/");
      PCPDynamicMeter_scanDir(meters, path);
      free(path);
   }

   /* next, search in home directory alongside htoprc */
   if (xdgConfigHome)
      path = String_cat(xdgConfigHome, "/htop/meters/");
   else if (home)
      path = String_cat(home, CONFIGDIR "/htop/meters/");
   else
      path = NULL;
   if (path) {
      PCPDynamicMeter_scanDir(meters, path);
      free(path);
   }

   /* next, search in the system meters directory */
   path = String_cat(sysconf, "/htop/meters/");
   PCPDynamicMeter_scanDir(meters, path);
   free(path);

   /* next, try the readonly system meters directory */
   path = String_cat(share, "/htop/meters/");
   PCPDynamicMeter_scanDir(meters, path);
   free(path);
}

PCPDynamicMeter.h

pcp/PCPDynamicMeter.h

function: PCPDynamicMeter_display

void PCPDynamicMeter_display(PCPDynamicMeter* this, const Meter* meter, RichString* out);

function: PCPDynamicMeter_enable

void PCPDynamicMeter_enable(PCPDynamicMeter* this);

function: PCPDynamicMeter_updateValues

void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter);

function: PCPDynamicMeters_done

void PCPDynamicMeters_done(Hashtable* table);

function: PCPDynamicMeters_init

void PCPDynamicMeters_init(PCPDynamicMeters* meters);

struct: PCPDynamicMeter

typedef struct PCPDynamicMeter_ {
   DynamicMeter super;
   PCPDynamicMetric* metrics;
   size_t totalMetrics;
} PCPDynamicMeter;

struct: PCPDynamicMeters

typedef struct PCPDynamicMeters_ {
   Hashtable* table;
   size_t count;  /* count of dynamic meters discovered by scan */
   size_t offset; /* start offset into the Platform metric array */
   size_t cursor; /* identifier allocator for each new metric used */
} PCPDynamicMeters;

struct: PCPDynamicMetric

typedef struct PCPDynamicMetric_ {
   size_t id; /* index into metric array */
   ColorElements color;
   char* name; /* derived metric name */
   char* label;
   char* suffix;
} PCPDynamicMetric;

PCPDynamicScreen.c

pcp/PCPDynamicScreen.c

function: formatFields

static char* formatFields(PCPDynamicScreen* screen) {
   char* columns = strdup("");

   for (size_t j = 0; j < screen->totalColumns; j++) {
      const PCPDynamicColumn* column = screen->columns[j];
      if (column->super.enabled == false)
         continue;
      char* prefix = columns;
      xAsprintf(&columns, "%s Dynamic(%s)", prefix, column->super.name);
      free(prefix);
   }

   return columns;
}

function: PCPDynamicScreen_addDynamicScreen

void PCPDynamicScreen_addDynamicScreen(PCPDynamicScreens* screens, ScreenSettings* ss) {
   PCPDynamicScreen* ds;

   for (size_t i = 0; i < screens->count; i++) {
      if ((ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i)) == NULL)
         continue;
      if (String_eq(ss->dynamic, ds->super.name) == false)
         continue;
      ss->table = &ds->table->super;
   }
}

function: PCPDynamicScreen_appendScreens

void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settings) {
   PCPDynamicScreen* ds;

   for (size_t i = 0; i < screens->count; i++) {
      if ((ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i)) == NULL)
         continue;
      if (ds->defaultEnabled == false)
         continue;
      const char* tab = ds->super.heading;
      Settings_newDynamicScreen(settings, tab, &ds->super, &ds->table->super);
   }
}

function: PCPDynamicScreen_appendTables

void PCPDynamicScreen_appendTables(PCPDynamicScreens* screens, Machine* host) {
   PCPDynamicScreen* ds;

   for (size_t i = 0; i < screens->count; i++) {
      if ((ds = (PCPDynamicScreen*)Hashtable_get(screens->table, i)) == NULL)
         continue;
      ds->table = InDomTable_new(host, ds->indom, ds->key);
   }
}

function: PCPDynamicScreen_done

static void PCPDynamicScreen_done(PCPDynamicScreen* ds) {
   DynamicScreen_done(&ds->super);
   Object_delete(ds->table);
   free(ds->columns);
}

function: PCPDynamicScreen_lookupMetric

static PCPDynamicColumn* PCPDynamicScreen_lookupMetric(PCPDynamicScreen* screen, const char* name) {
   PCPDynamicColumn* column = NULL;
   if ((strlen(name) + strlen(screen->super.name) + 1) >= sizeof(column->super.name)) /* colon */
      return NULL;

   char* metricName = NULL;
   xAsprintf(&metricName, "htop.screen.%s.%s", screen->super.name, name);

   for (size_t i = 0; i < screen->totalColumns; i++) {
      column = screen->columns[i];
      if (String_eq(column->metricName, metricName)) {
         free(metricName);
         return column;
      }
   }

   /* not an existing column in this screen - create it and add to the list */
   column = xCalloc(1, sizeof(PCPDynamicColumn));
   xSnprintf(column->super.name, sizeof(column->super.name), "%s:%s", screen->super.name, name);
   column->super.table = &screen->table->super;
   column->metricName = metricName;
   column->super.enabled = true;

   size_t n = screen->totalColumns + 1;
   screen->columns = xReallocArray(screen->columns, n, sizeof(PCPDynamicColumn*));
   screen->columns[n - 1] = column;
   screen->totalColumns = n;

   return column;
}

function: PCPDynamicScreen_new

static PCPDynamicScreen* PCPDynamicScreen_new(PCPDynamicScreens* screens, const char* name) {
   PCPDynamicScreen* screen = xCalloc(1, sizeof(*screen));
   String_safeStrncpy(screen->super.name, name, sizeof(screen->super.name));
   screen->defaultEnabled = true;

   size_t id = screens->count;
   Hashtable_put(screens->table, id, screen);
   screens->count++;

   return screen;
}

function: PCPDynamicScreen_parseColumn

function: PCPDynamicScreen_parseFile

function: PCPDynamicScreen_scanDir

static void PCPDynamicScreen_scanDir(PCPDynamicScreens* screens, char* path) {
   DIR* dir = opendir(path);
   if (!dir)
      return;

   struct dirent* dirent;
   while ((dirent = readdir(dir)) != NULL) {
      if (dirent->d_name[0] == '.')
         continue;

      char* file = String_cat(path, dirent->d_name);
      PCPDynamicScreen_parseFile(screens, file);
      free(file);
   }
   closedir(dir);
}

function: PCPDynamicScreen_uniqueName

static bool PCPDynamicScreen_uniqueName(char* key, PCPDynamicScreens* screens) {
   return !DynamicScreen_search(screens->table, key, NULL);
}

function: PCPDynamicScreen_validateScreenName

static bool PCPDynamicScreen_validateScreenName(char* key, const char* path, unsigned int line) {
   char* p = key;
   char* end = strrchr(key, ']');

   if (end) {
      *end = '\0';
   } else {
      fprintf(stderr,
            "%s: no closing brace on screen name at %s line %u\n\"%s\"",
            pmGetProgname(), path, line, key);
      return false;
   }

   while (*p) {
      if (p == key) {
         if (!isalpha(*p) && *p != '_')
            break;
      } else {
         if (!isalnum(*p) && *p != '_')
            break;
      }
      p++;
   }
   if (*p != '\0') { /* badness */
      fprintf(stderr,
            "%s: invalid screen name at %s line %u\n\"%s\"",
            pmGetProgname(), path, line, key);
      return false;
   }
   return true;
}

function: PCPDynamicScreens_addAvailableColumns

void PCPDynamicScreens_addAvailableColumns(Panel* availableColumns, Hashtable* screens, const char* screen) {
   Vector_prune(availableColumns->items);

   bool success;
   unsigned int key;
   success = DynamicScreen_search(screens, screen, &key);
   if (!success)
      return;

   PCPDynamicScreen* dynamicScreen = Hashtable_get(screens, key);
   if (!dynamicScreen)
      return;

   for (unsigned int j = 0; j < dynamicScreen->totalColumns; j++) {
      PCPDynamicColumn* column = dynamicScreen->columns[j];
      const char* title = column->super.heading ? column->super.heading : column->super.name;
      const char* text = column->super.description ? column->super.description : column->super.caption;
      char description[256];
      if (text)
         xSnprintf(description, sizeof(description), "%s - %s", title, text);
      else
         xSnprintf(description, sizeof(description), "%s", title);
      Panel_add(availableColumns, (Object*) ListItem_new(description, j));
   }
}

function: PCPDynamicScreens_appendDynamicColumns

static void PCPDynamicScreens_appendDynamicColumns(PCPDynamicScreens* screens, PCPDynamicColumns* columns) {
   for (size_t i = 0; i < screens->count; i++) {
      PCPDynamicScreen* screen = Hashtable_get(screens->table, i);
      if (!screen)
         return;

      /* setup default fields (columns) based on configuration */
      for (size_t j = 0; j < screen->totalColumns; j++) {
         PCPDynamicColumn* column = screen->columns[j];

         column->id = columns->offset + columns->cursor;
         columns->cursor++;
         Platform_addMetric(column->id, column->metricName);

         size_t id = columns->count + LAST_PROCESSFIELD;
         Hashtable_put(columns->table, id, column);
         columns->count++;

         if (j == 0) {
            const pmDesc* desc = Metric_desc(column->id);
            assert(desc->indom != PM_INDOM_NULL);
            screen->indom = desc->indom;
            screen->key = column->id;
         }
      }
      screen->super.columnKeys = formatFields(screen);
   }
}

function: PCPDynamicScreens_done

void PCPDynamicScreens_done(Hashtable* table) {
   Hashtable_foreach(table, PCPDynamicScreens_free, NULL);
}

function: PCPDynamicScreens_free

static void PCPDynamicScreens_free(ATTR_UNUSED ht_key_t key, void* value, ATTR_UNUSED void* data) {
   PCPDynamicScreen* ds = (PCPDynamicScreen*) value;
   PCPDynamicScreen_done(ds);
}

function: PCPDynamicScreens_init

void PCPDynamicScreens_init(PCPDynamicScreens* screens, PCPDynamicColumns* columns) {
   const char* share = pmGetConfig("PCP_SHARE_DIR");
   const char* sysconf = pmGetConfig("PCP_SYSCONF_DIR");
   const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
   const char* override = getenv("PCP_HTOP_DIR");
   const char* home = getenv("HOME");
   char* path;

   screens->table = Hashtable_new(0, true);

   /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */
   if (override) {
      path = String_cat(override, "/screens/");
      PCPDynamicScreen_scanDir(screens, path);
      free(path);
   }

   /* next, search in home directory alongside htoprc */
   if (xdgConfigHome)
      path = String_cat(xdgConfigHome, "/htop/screens/");
   else if (home)
      path = String_cat(home, CONFIGDIR "/htop/screens/");
   else
      path = NULL;
   if (path) {
      PCPDynamicScreen_scanDir(screens, path);
      free(path);
   }

   /* next, search in the system screens directory */
   path = String_cat(sysconf, "/htop/screens/");
   PCPDynamicScreen_scanDir(screens, path);
   free(path);

   /* next, try the readonly system screens directory */
   path = String_cat(share, "/htop/screens/");
   PCPDynamicScreen_scanDir(screens, path);
   free(path);

   /* establish internal metric identifier mappings */
   PCPDynamicScreens_appendDynamicColumns(screens, columns);
}

PCPDynamicScreen.h

pcp/PCPDynamicScreen.h

function: PCPDynamicScreen_addDynamicScreen

void PCPDynamicScreen_addDynamicScreen(PCPDynamicScreens* screens, ScreenSettings* ss);

function: PCPDynamicScreen_appendScreens

void PCPDynamicScreen_appendScreens(PCPDynamicScreens* screens, Settings* settings);

function: PCPDynamicScreen_appendTables

void PCPDynamicScreen_appendTables(PCPDynamicScreens* screens, Machine* host);

function: PCPDynamicScreens_addAvailableColumns

void PCPDynamicScreens_addAvailableColumns(Panel* availableColumns, Hashtable* screens, const char* screen);

function: PCPDynamicScreens_done

void PCPDynamicScreens_done(Hashtable* table);

function: PCPDynamicScreens_init

void PCPDynamicScreens_init(PCPDynamicScreens* screens, struct PCPDynamicColumns_* columns);

struct: PCPDynamicScreen_

typedef struct PCPDynamicScreen_ {
   DynamicScreen super;

   struct InDomTable_* table;
   struct PCPDynamicColumn_** columns;
   size_t totalColumns;

   unsigned int indom;  /* instance domain number */
   unsigned int key;  /* PCPMetric identifier */

   bool defaultEnabled; /* enabled setting from configuration file */
   /* at runtime enabled screens have entries in settings->screens */
} PCPDynamicScreen;

struct: PCPDynamicScreens_

typedef struct PCPDynamicScreens_ {
   Hashtable* table;
   size_t count;  /* count of dynamic screens discovered from scan */
} PCPDynamicScreens;

PCPMachine.c

pcp/PCPMachine.c

function: Machine_delete

void Machine_delete(Machine* super) {
   PCPMachine* this = (PCPMachine*) super;
   Machine_done(super);
   free(this->values);
   for (unsigned int i = 0; i < super->existingCPUs; i++)
      free(this->percpu[i]);
   free(this->percpu);
   free(this->cpu);
   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* host, unsigned int id) {
   assert(id < host->existingCPUs);
   (void) host;

   pmAtomValue value;
   if (Metric_instance(PCP_PERCPU_SYSTEM, id, id, &value, PM_TYPE_U32))
      return true;
   return false;
}

function: Machine_new

Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
   PCPMachine* this = xCalloc(1, sizeof(PCPMachine));
   Machine* super = &this->super;

   Machine_init(super, usersTable, userId);

   struct timeval timestamp;
   gettimeofday(&timestamp, NULL);
   this->timestamp = pmtimevalToReal(&timestamp);

   this->cpu = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
   PCPMachine_updateCPUcount(this);

   Platform_updateTables(super);

   return super;
}

function: Machine_scan

void Machine_scan(Machine* super) {
   PCPMachine* host = (PCPMachine*) super;
   const Settings* settings = super->settings;
   uint32_t flags = settings->ss->flags;
   bool flagged;

   for (int metric = PCP_PROC_PID; metric < PCP_METRIC_COUNT; metric++)
      Metric_enable(metric, true);

   flagged = settings->showCPUFrequency;
   Metric_enable(PCP_HINV_CPUCLOCK, flagged);
   flagged = flags & PROCESS_FLAG_LINUX_CGROUP;
   Metric_enable(PCP_PROC_CGROUPS, flagged);
   flagged = flags & PROCESS_FLAG_LINUX_OOM;
   Metric_enable(PCP_PROC_OOMSCORE, flagged);
   flagged = flags & PROCESS_FLAG_LINUX_CTXT;
   Metric_enable(PCP_PROC_VCTXSW, flagged);
   Metric_enable(PCP_PROC_NVCTXSW, flagged);
   flagged = flags & PROCESS_FLAG_LINUX_SECATTR;
   Metric_enable(PCP_PROC_LABELS, flagged);
   flagged = flags & PROCESS_FLAG_LINUX_AUTOGROUP;
   Metric_enable(PCP_PROC_AUTOGROUP_ID, flagged);
   Metric_enable(PCP_PROC_AUTOGROUP_NICE, flagged);

   /* Sample smaps metrics on every second pass to improve performance */
   host->smaps_flag = !!host->smaps_flag;
   Metric_enable(PCP_PROC_SMAPS_PSS, host->smaps_flag);
   Metric_enable(PCP_PROC_SMAPS_SWAP, host->smaps_flag);
   Metric_enable(PCP_PROC_SMAPS_SWAPPSS, host->smaps_flag);

   struct timeval timestamp;
   if (Metric_fetch(&timestamp) != true)
      return;

   double sample = host->timestamp;
   host->timestamp = pmtimevalToReal(&timestamp);
   host->period = (host->timestamp - sample) * 100;

   PCPMachine_scan(host);
}

function: PCPMachine_backupCPUTime

/* make copies of previously sampled values to avoid overwrite */
static inline void PCPMachine_backupCPUTime(pmAtomValue* values) {
   /* the PERIOD fields (must) mirror the TIME fields */
   for (int metric = CPU_TOTAL_TIME; metric < CPU_TOTAL_PERIOD; metric++) {
      values[metric + CPU_TOTAL_PERIOD] = values[metric];
   }
}

function: PCPMachine_deriveCPUTime

function: PCPMachine_saveCPUTimePeriod

static inline void PCPMachine_saveCPUTimePeriod(pmAtomValue* values, CPUMetric previous, pmAtomValue* latest) {
   pmAtomValue* value;

   /* new value for period */
   value = &values[previous];
   if (latest->ull > value->ull)
      value->ull = latest->ull - value->ull;
   else
      value->ull = 0;

   /* new value for time */
   value = &values[previous - CPU_TOTAL_PERIOD];
   value->ull = latest->ull;
}

function: PCPMachine_scan

static void PCPMachine_scan(PCPMachine* this) {
   Machine* super = &this->super;

   PCPMachine_updateMemoryInfo(super);
   PCPMachine_updateCPUcount(this);

   PCPMachine_backupCPUTime(this->cpu);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_USER, CPU_USER_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_NICE, CPU_NICE_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_SYSTEM, CPU_SYSTEM_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_IDLE, CPU_IDLE_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_IOWAIT, CPU_IOWAIT_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_IRQ, CPU_IRQ_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_STEAL, CPU_STEAL_TIME);
   PCPMachine_updateAllCPUTime(this, PCP_CPU_GUEST, CPU_GUEST_TIME);
   PCPMachine_deriveCPUTime(this->cpu);

   for (unsigned int i = 0; i < super->existingCPUs; i++)
      PCPMachine_backupCPUTime(this->percpu[i]);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_USER, CPU_USER_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_NICE, CPU_NICE_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SYSTEM, CPU_SYSTEM_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IDLE, CPU_IDLE_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IOWAIT, CPU_IOWAIT_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_IRQ, CPU_IRQ_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_SOFTIRQ, CPU_SOFTIRQ_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_STEAL, CPU_STEAL_TIME);
   PCPMachine_updatePerCPUTime(this, PCP_PERCPU_GUEST, CPU_GUEST_TIME);
   for (unsigned int i = 0; i < super->existingCPUs; i++)
      PCPMachine_deriveCPUTime(this->percpu[i]);

   if (super->settings->showCPUFrequency)
      PCPMachine_updatePerCPUReal(this, PCP_HINV_CPUCLOCK, CPU_FREQUENCY);

   PCPMachine_scanZfsArcstats(this);
   PCPMachine_scanZswapInfo(this);
}

function: PCPMachine_scanZfsArcstats

static inline void PCPMachine_scanZfsArcstats(PCPMachine* this) {
   unsigned long long int dbufSize = 0;
   unsigned long long int dnodeSize = 0;
   unsigned long long int bonusSize = 0;
   pmAtomValue value;

   memset(&this->zfs, 0, sizeof(ZfsArcStats));
   if (Metric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.anon = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64))
      this->zfs.min = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
      this->zfs.max = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
      bonusSize = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_DBUF_SIZE, &value, 1, PM_TYPE_U64))
      dbufSize = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_DNODE_SIZE, &value, 1, PM_TYPE_U64))
      dnodeSize = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_COMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.compressed = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_UNCOMPRESSED_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.uncompressed = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_HDR_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.header = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_MFU_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.MFU = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_MRU_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.MRU = value.ull / ONE_K;
   if (Metric_values(PCP_ZFS_ARC_SIZE, &value, 1, PM_TYPE_U64))
      this->zfs.size = value.ull / ONE_K;

   this->zfs.other = (dbufSize + dnodeSize + bonusSize) / ONE_K;
   this->zfs.enabled = (this->zfs.size > 0);
   this->zfs.isCompressed = (this->zfs.compressed > 0);
}

function: PCPMachine_scanZswapInfo

static inline void PCPMachine_scanZswapInfo(PCPMachine* this) {
   pmAtomValue value;

   memset(&this->zswap, 0, sizeof(ZswapStats));
   if (Metric_values(PCP_MEM_ZSWAP, &value, 1, PM_TYPE_U64))
      this->zswap.usedZswapComp = value.ull;
   if (Metric_values(PCP_MEM_ZSWAPPED, &value, 1, PM_TYPE_U64))
      this->zswap.usedZswapOrig = value.ull;
}

function: PCPMachine_updateAllCPUTime

static void PCPMachine_updateAllCPUTime(PCPMachine* this, Metric metric, CPUMetric cpumetric)
{
   pmAtomValue* value = &this->cpu[cpumetric];
   if (Metric_values(metric, value, 1, PM_TYPE_U64) == NULL)
      memset(value, 0, sizeof(pmAtomValue));
}

function: PCPMachine_updateCPUcount

static void PCPMachine_updateCPUcount(PCPMachine* this) {
   Machine* super = &this->super;
   super->activeCPUs = Metric_instanceCount(PCP_PERCPU_SYSTEM);
   unsigned int cpus = Platform_getMaxCPU();
   if (cpus == super->existingCPUs)
      return;
   if (cpus == 0)
      cpus = super->activeCPUs;
   if (cpus <= 1)
      cpus = super->activeCPUs = 1;
   super->existingCPUs = cpus;

   free(this->percpu);
   free(this->values);

   this->percpu = xCalloc(cpus, sizeof(pmAtomValue*));
   for (unsigned int i = 0; i < cpus; i++)
      this->percpu[i] = xCalloc(CPU_METRIC_COUNT, sizeof(pmAtomValue));
   this->values = xCalloc(cpus, sizeof(pmAtomValue));
}

function: PCPMachine_updateMemoryInfo

static void PCPMachine_updateMemoryInfo(Machine* host) {
   unsigned long long int freeMem = 0;
   unsigned long long int swapFreeMem = 0;
   unsigned long long int sreclaimableMem = 0;
   host->totalMem = host->usedMem = host->cachedMem = 0;
   host->usedSwap = host->totalSwap = host->sharedMem = 0;

   pmAtomValue value;
   if (Metric_values(PCP_MEM_TOTAL, &value, 1, PM_TYPE_U64) != NULL)
      host->totalMem = value.ull;
   if (Metric_values(PCP_MEM_FREE, &value, 1, PM_TYPE_U64) != NULL)
      freeMem = value.ull;
   if (Metric_values(PCP_MEM_BUFFERS, &value, 1, PM_TYPE_U64) != NULL)
      host->buffersMem = value.ull;
   if (Metric_values(PCP_MEM_SRECLAIM, &value, 1, PM_TYPE_U64) != NULL)
      sreclaimableMem = value.ull;
   if (Metric_values(PCP_MEM_SHARED, &value, 1, PM_TYPE_U64) != NULL)
      host->sharedMem = value.ull;
   if (Metric_values(PCP_MEM_CACHED, &value, 1, PM_TYPE_U64) != NULL)
      host->cachedMem = value.ull + sreclaimableMem - host->sharedMem;
   const memory_t usedDiff = freeMem + host->cachedMem + sreclaimableMem + host->buffersMem;
   host->usedMem = (host->totalMem >= usedDiff) ?
           host->totalMem - usedDiff : host->totalMem - freeMem;
   if (Metric_values(PCP_MEM_AVAILABLE, &value, 1, PM_TYPE_U64) != NULL)
      host->availableMem = MINIMUM(value.ull, host->totalMem);
   else
      host->availableMem = freeMem;
   if (Metric_values(PCP_MEM_SWAPFREE, &value, 1, PM_TYPE_U64) != NULL)
      swapFreeMem = value.ull;
   if (Metric_values(PCP_MEM_SWAPTOTAL, &value, 1, PM_TYPE_U64) != NULL)
      host->totalSwap = value.ull;
   if (Metric_values(PCP_MEM_SWAPCACHED, &value, 1, PM_TYPE_U64) != NULL)
      host->cachedSwap = value.ull;
   host->usedSwap = host->totalSwap - swapFreeMem - host->cachedSwap;
}

function: PCPMachine_updatePerCPUReal

static void PCPMachine_updatePerCPUReal(PCPMachine* this, Metric metric, CPUMetric cpumetric)
{
   int cpus = this->super.existingCPUs;
   if (Metric_values(metric, this->values, cpus, PM_TYPE_DOUBLE) == NULL)
      memset(this->values, 0, cpus * sizeof(pmAtomValue));
   for (int i = 0; i < cpus; i++)
      this->percpu[i][cpumetric].d = this->values[i].d;
}

function: PCPMachine_updatePerCPUTime

static void PCPMachine_updatePerCPUTime(PCPMachine* this, Metric metric, CPUMetric cpumetric)
{
   int cpus = this->super.existingCPUs;
   if (Metric_values(metric, this->values, cpus, PM_TYPE_U64) == NULL)
      memset(this->values, 0, cpus * sizeof(pmAtomValue));
   for (int i = 0; i < cpus; i++)
      this->percpu[i][cpumetric].ull = this->values[i].ull;
}

PCPMachine.h

pcp/PCPMachine.h

enum: CPUMetric

typedef enum CPUMetric_ {
   CPU_TOTAL_TIME,
   CPU_USER_TIME,
   CPU_SYSTEM_TIME,
   CPU_SYSTEM_ALL_TIME,
   CPU_IDLE_ALL_TIME,
   CPU_IDLE_TIME,
   CPU_NICE_TIME,
   CPU_IOWAIT_TIME,
   CPU_IRQ_TIME,
   CPU_SOFTIRQ_TIME,
   CPU_STEAL_TIME,
   CPU_GUEST_TIME,
   CPU_GUESTNICE_TIME,

   CPU_TOTAL_PERIOD,
   CPU_USER_PERIOD,
   CPU_SYSTEM_PERIOD,
   CPU_SYSTEM_ALL_PERIOD,
   CPU_IDLE_ALL_PERIOD,
   CPU_IDLE_PERIOD,
   CPU_NICE_PERIOD,
   CPU_IOWAIT_PERIOD,
   CPU_IRQ_PERIOD,
   CPU_SOFTIRQ_PERIOD,
   CPU_STEAL_PERIOD,
   CPU_GUEST_PERIOD,
   CPU_GUESTNICE_PERIOD,

   CPU_FREQUENCY,

   CPU_METRIC_COUNT
} CPUMetric;

struct: PCPMachine

typedef struct PCPMachine_ {
   Machine super;
   int smaps_flag;
   double period;
   double timestamp;     /* previous sample timestamp */

   pmAtomValue* cpu;     /* aggregate values for each metric */
   pmAtomValue** percpu; /* per-processor values for each metric */
   pmAtomValue* values;  /* per-processor buffer for just one metric */

   ZfsArcStats zfs;
   /*ZramStats zram; -- not needed, calculated in-line in Platform.c */
   ZswapStats zswap;
} PCPMachine;

PCPProcess.c

pcp/PCPProcess.c

function: PCPProcess_compareByKey

function: PCPProcess_new

Process* PCPProcess_new(const Machine* host) {
   PCPProcess* this = xCalloc(1, sizeof(PCPProcess));
   Object_setClass(this, Class(PCPProcess));
   Process_init(&this->super, host);
   return (Process*)this;
}

function: PCPProcess_printDelay

static void PCPProcess_printDelay(float delay_percent, char* buffer, size_t n) {
   if (isNonnegative(delay_percent)) {
      xSnprintf(buffer, n, "%4.1f  ", delay_percent);
   } else {
      xSnprintf(buffer, n, " N/A  ");
   }
}

function: PCPProcess_rowWriteField

function: PCPProcess_totalIORate

static double PCPProcess_totalIORate(const PCPProcess* pp) {
   double totalRate = NAN;
   if (isNonnegative(pp->io_rate_read_bps)) {
      totalRate = pp->io_rate_read_bps;
      if (isNonnegative(pp->io_rate_write_bps)) {
         totalRate += pp->io_rate_write_bps;
      }
   } else if (isNonnegative(pp->io_rate_write_bps)) {
      totalRate = pp->io_rate_write_bps;
   }
   return totalRate;
}

function: Process_delete

void Process_delete(Object* cast) {
   PCPProcess* this = (PCPProcess*) cast;
   Process_done((Process*)cast);
   free(this->cgroup_short);
   free(this->cgroup);
   free(this->secattr);
   free(this);
}

global constant struct: PCPProcess_class

const ProcessClass PCPProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = PCPProcess_rowWriteField,
   },
   .compareByKey = PCPProcess_compareByKey,
};

global variable: Process_fields

PCPProcess.h

pcp/PCPProcess.h

function: PCPProcess_new

Process* PCPProcess_new(const Machine* host);

function: Process_delete

void Process_delete(Object* cast);

function: Process_isThread

bool Process_isThread(const Process* this);

macro: PROCESS_FLAG_LINUX_AUTOGROUP

#define PROCESS_FLAG_LINUX_AUTOGROUP 0x00080000

macro: PROCESS_FLAG_LINUX_CGROUP

#define PROCESS_FLAG_LINUX_CGROUP    0x00000800

macro: PROCESS_FLAG_LINUX_CTXT

#define PROCESS_FLAG_LINUX_CTXT      0x00004000

macro: PROCESS_FLAG_LINUX_OOM

#define PROCESS_FLAG_LINUX_OOM       0x00001000

macro: PROCESS_FLAG_LINUX_SECATTR

#define PROCESS_FLAG_LINUX_SECATTR   0x00008000

macro: PROCESS_FLAG_LINUX_SMAPS

#define PROCESS_FLAG_LINUX_SMAPS     0x00002000

struct: PCPProcess

typedef struct PCPProcess_ {
   Process super;

   /* default result offset to use for searching proc metrics */
   unsigned int offset;

   unsigned long int cminflt;
   unsigned long int cmajflt;
   unsigned long long int utime;
   unsigned long long int stime;
   unsigned long long int cutime;
   unsigned long long int cstime;
   long m_share;
   long m_priv;
   long m_pss;
   long m_swap;
   long m_psswp;
   long m_trs;
   long m_drs;
   long m_lrs;
   long m_dt;

   /* Data read (in kilobytes) */
   unsigned long long io_rchar;

   /* Data written (in kilobytes) */
   unsigned long long io_wchar;

   /* Number of read(2) syscalls */
   unsigned long long io_syscr;

   /* Number of write(2) syscalls */
   unsigned long long io_syscw;

   /* Storage data read (in kilobytes) */
   unsigned long long io_read_bytes;

   /* Storage data written (in kilobytes) */
   unsigned long long io_write_bytes;

   /* Storage data cancelled (in kilobytes) */
   unsigned long long io_cancelled_write_bytes;

   /* Point in time of last io scan (in seconds elapsed since the Epoch) */
   unsigned long long io_last_scan_time;

   double io_rate_read_bps;
   double io_rate_write_bps;
   char* cgroup;
   char* cgroup_short;
   char* container_short;
   long int autogroup_id;
   int autogroup_nice;
   unsigned int oom;
   unsigned long long int delay_read_time;
   unsigned long long cpu_delay_total;
   unsigned long long blkio_delay_total;
   unsigned long long swapin_delay_total;
   float cpu_delay_percent;
   float blkio_delay_percent;
   float swapin_delay_percent;
   unsigned long ctxt_total;
   unsigned long ctxt_diff;
   char* secattr;
   unsigned long long int last_mlrs_calctime;
} PCPProcess;

variable: PCPProcess_class

extern const ProcessClass PCPProcess_class;

variable: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

PCPProcessTable.c

pcp/PCPProcessTable.c

function: Metric_instance_char

static inline char Metric_instance_char(int metric, int pid, int offset, char fallback) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_STRING)) {
      char uchar = value.cp[0];
      free(value.cp);
      return uchar;
   }
   return fallback;
}

function: Metric_instance_ONE_K

static inline unsigned long long Metric_instance_ONE_K(int metric, int pid, int offset) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U64))
      return value.ull / ONE_K;
   return ULLONG_MAX;
}

function: Metric_instance_s32

static inline long Metric_instance_s32(int metric, int pid, int offset, long fallback) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_32))
      return value.l;
   return fallback;
}

function: Metric_instance_s64

static inline long long Metric_instance_s64(int metric, int pid, int offset, long long fallback) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_64))
      return value.l;
   return fallback;
}

function: Metric_instance_time

static inline unsigned long long Metric_instance_time(int metric, int pid, int offset) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U64))
      return value.ull / 10;
   return 0;
}

function: Metric_instance_u32

static inline unsigned long Metric_instance_u32(int metric, int pid, int offset, unsigned long fallback) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U32))
      return value.ul;
   return fallback;
}

function: Metric_instance_u64

static inline unsigned long long Metric_instance_u64(int metric, int pid, int offset, unsigned long long fallback) {
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_U64))
      return value.ull;
   return fallback;
}

function: PCPProcessTable_getProcessState

static inline ProcessState PCPProcessTable_getProcessState(char state) {
   switch (state) {
      case '?': return UNKNOWN;
      case 'R': return RUNNING;
      case 'W': return WAITING;
      case 'D': return UNINTERRUPTIBLE_WAIT;
      case 'P': return PAGING;
      case 'T': return STOPPED;
      case 't': return TRACED;
      case 'Z': return ZOMBIE;
      case 'X': return DEFUNCT;
      case 'I': return IDLE;
      case 'S': return SLEEPING;
      default: return UNKNOWN;
   }
}

function: PCPProcessTable_readAutogroup

static void PCPProcessTable_readAutogroup(PCPProcess* pp, int pid, int offset) {
   pp->autogroup_id = Metric_instance_s64(PCP_PROC_AUTOGROUP_ID, pid, offset, -1);
   pp->autogroup_nice = Metric_instance_s32(PCP_PROC_AUTOGROUP_NICE, pid, offset, 0);
}

function: PCPProcessTable_readCGroups

static void PCPProcessTable_readCGroups(PCPProcess* pp, int pid, int offset) {
   pp->cgroup = setString(PCP_PROC_CGROUPS, pid, offset, pp->cgroup);

   if (pp->cgroup) {
      char* cgroup_short = CGroup_filterName(pp->cgroup);
      if (cgroup_short) {
         Row_updateFieldWidth(CCGROUP, strlen(cgroup_short));
         free_and_xStrdup(&pp->cgroup_short, cgroup_short);
         free(cgroup_short);
      } else {
         //CCGROUP is alias to normal CGROUP if shortening fails
         Row_updateFieldWidth(CCGROUP, strlen(pp->cgroup));
         free(pp->cgroup_short);
         pp->cgroup_short = NULL;
      }

      char* container_short = CGroup_filterName(pp->cgroup);
      if (container_short) {
         Row_updateFieldWidth(CONTAINER, strlen(container_short));
         free_and_xStrdup(&pp->container_short, container_short);
         free(container_short);
      } else {
         Row_updateFieldWidth(CONTAINER, strlen("N/A"));
         free(pp->container_short);
         pp->container_short = NULL;
      }
   } else {
      free(pp->cgroup_short);
      pp->cgroup_short = NULL;

      free(pp->container_short);
      pp->container_short = NULL;
   }
}

function: PCPProcessTable_readCtxtData

static void PCPProcessTable_readCtxtData(PCPProcess* pp, int pid, int offset) {
   pmAtomValue value;
   unsigned long ctxt = 0;

   if (Metric_instance(PCP_PROC_VCTXSW, pid, offset, &value, PM_TYPE_U32))
      ctxt += value.ul;
   if (Metric_instance(PCP_PROC_NVCTXSW, pid, offset, &value, PM_TYPE_U32))
      ctxt += value.ul;

   pp->ctxt_diff = ctxt > pp->ctxt_total ? ctxt - pp->ctxt_total : 0;
   pp->ctxt_total = ctxt;
}

function: PCPProcessTable_readCwd

static void PCPProcessTable_readCwd(PCPProcess* pp, int pid, int offset) {
   pp->super.procCwd = setString(PCP_PROC_CWD, pid, offset, pp->super.procCwd);
}

function: PCPProcessTable_readOomData

static void PCPProcessTable_readOomData(PCPProcess* pp, int pid, int offset) {
   pp->oom = Metric_instance_u32(PCP_PROC_OOMSCORE, pid, offset, 0);
}

function: PCPProcessTable_readSecattrData

static void PCPProcessTable_readSecattrData(PCPProcess* pp, int pid, int offset) {
   pp->secattr = setString(PCP_PROC_LABELS, pid, offset, pp->secattr);
}

function: PCPProcessTable_updateCmdline

static void PCPProcessTable_updateCmdline(Process* process, int pid, int offset, const char* comm) {
   pmAtomValue value;
   if (!Metric_instance(PCP_PROC_PSARGS, pid, offset, &value, PM_TYPE_STRING)) {
      if (process->state != ZOMBIE)
         process->isKernelThread = true;
      Process_updateCmdline(process, NULL, 0, 0);
      return;
   }

   char* command = value.cp;
   int length = strlen(command);
   if (command[0] != '(') {
      process->isKernelThread = false;
   } else {
      ++command;
      --length;
      if (command[length - 1] == ')')
         command[--length] = '\0';
      process->isKernelThread = true;
   }

   int tokenEnd = 0;
   int tokenStart = 0;
   bool argSepSpace = false;

   for (int i = 0; i < length; i++) {
      /* htop considers the next character after the last / that is before
       * basenameOffset, as the start of the basename in cmdline - see
       * Process_writeCommand */
      if (command[i] == '/')
         tokenStart = i + 1;
      /* special-case arguments for problematic situations like "find /" */
      if (command[i] <= ' ')
         argSepSpace = true;
   }
   tokenEnd = length;
   if (argSepSpace)
      tokenStart = 0;

   Process_updateCmdline(process, command, tokenStart, tokenEnd);
   free(value.cp);

   Process_updateComm(process, comm);

   if (Metric_instance(PCP_PROC_EXE, pid, offset, &value, PM_TYPE_STRING)) {
      Process_updateExe(process, value.cp[0] ? value.cp : NULL);
      free(value.cp);
   }
}

function: PCPProcessTable_updateID

static void PCPProcessTable_updateID(Process* process, int pid, int offset) {
   Process_setThreadGroup(process, Metric_instance_u32(PCP_PROC_TGID, pid, offset, 1));
   Process_setParent(process, Metric_instance_u32(PCP_PROC_PPID, pid, offset, 1));
   process->state = PCPProcessTable_getProcessState(Metric_instance_char(PCP_PROC_STATE, pid, offset, '?'));
}

function: PCPProcessTable_updateInfo

static void PCPProcessTable_updateInfo(PCPProcess* pp, int pid, int offset, char* command, size_t commLen) {
   Process* process = &pp->super;
   pmAtomValue value;

   if (!Metric_instance(PCP_PROC_CMD, pid, offset, &value, PM_TYPE_STRING))
      value.cp = xStrdup("<unknown>");
   String_safeStrncpy(command, value.cp, commLen);
   free(value.cp);

   process->pgrp = Metric_instance_u32(PCP_PROC_PGRP, pid, offset, 0);
   process->session = Metric_instance_u32(PCP_PROC_SESSION, pid, offset, 0);
   process->tty_nr = Metric_instance_u32(PCP_PROC_TTY, pid, offset, 0);
   process->tpgid = Metric_instance_u32(PCP_PROC_TTYPGRP, pid, offset, 0);
   process->minflt = Metric_instance_u32(PCP_PROC_MINFLT, pid, offset, 0);
   pp->cminflt = Metric_instance_u32(PCP_PROC_CMINFLT, pid, offset, 0);
   process->majflt = Metric_instance_u32(PCP_PROC_MAJFLT, pid, offset, 0);
   pp->cmajflt = Metric_instance_u32(PCP_PROC_CMAJFLT, pid, offset, 0);
   pp->utime = Metric_instance_time(PCP_PROC_UTIME, pid, offset);
   pp->stime = Metric_instance_time(PCP_PROC_STIME, pid, offset);
   pp->cutime = Metric_instance_time(PCP_PROC_CUTIME, pid, offset);
   pp->cstime = Metric_instance_time(PCP_PROC_CSTIME, pid, offset);
   process->priority = Metric_instance_u32(PCP_PROC_PRIORITY, pid, offset, 0);
   process->nice = Metric_instance_s32(PCP_PROC_NICE, pid, offset, 0);
   process->nlwp = Metric_instance_u32(PCP_PROC_THREADS, pid, offset, 0);
   process->starttime_ctime = Metric_instance_time(PCP_PROC_STARTTIME, pid, offset);
   process->processor = Metric_instance_u32(PCP_PROC_PROCESSOR, pid, offset, 0);

   process->time = pp->utime + pp->stime;
}

function: PCPProcessTable_updateIO

static void PCPProcessTable_updateIO(PCPProcess* pp, int pid, int offset, unsigned long long now) {
   pmAtomValue value;

   pp->io_rchar = Metric_instance_ONE_K(PCP_PROC_IO_RCHAR, pid, offset);
   pp->io_wchar = Metric_instance_ONE_K(PCP_PROC_IO_WCHAR, pid, offset);
   pp->io_syscr = Metric_instance_u64(PCP_PROC_IO_SYSCR, pid, offset, ULLONG_MAX);
   pp->io_syscw = Metric_instance_u64(PCP_PROC_IO_SYSCW, pid, offset, ULLONG_MAX);
   pp->io_cancelled_write_bytes = Metric_instance_ONE_K(PCP_PROC_IO_CANCELLED, pid, offset);

   if (Metric_instance(PCP_PROC_IO_READB, pid, offset, &value, PM_TYPE_U64)) {
      unsigned long long last_read = pp->io_read_bytes;
      pp->io_read_bytes = value.ull / ONE_K;
      pp->io_rate_read_bps = ONE_K * (pp->io_read_bytes - last_read) /
                                     (now - pp->io_last_scan_time);
   } else {
      pp->io_read_bytes = ULLONG_MAX;
      pp->io_rate_read_bps = NAN;
   }

   if (Metric_instance(PCP_PROC_IO_WRITEB, pid, offset, &value, PM_TYPE_U64)) {
      unsigned long long last_write = pp->io_write_bytes;
      pp->io_write_bytes = value.ull;
      pp->io_rate_write_bps = ONE_K * (pp->io_write_bytes - last_write) /
                                      (now - pp->io_last_scan_time);
   } else {
      pp->io_write_bytes = ULLONG_MAX;
      pp->io_rate_write_bps = NAN;
   }

   pp->io_last_scan_time = now;
}

function: PCPProcessTable_updateMemory

static void PCPProcessTable_updateMemory(PCPProcess* pp, int pid, int offset) {
   pp->super.m_virt = Metric_instance_u32(PCP_PROC_MEM_SIZE, pid, offset, 0);
   pp->super.m_resident = Metric_instance_u32(PCP_PROC_MEM_RSS, pid, offset, 0);
   pp->m_share = Metric_instance_u32(PCP_PROC_MEM_SHARE, pid, offset, 0);
   pp->m_priv = pp->super.m_resident - pp->m_share;
   pp->m_trs = Metric_instance_u32(PCP_PROC_MEM_TEXTRS, pid, offset, 0);
   pp->m_lrs = Metric_instance_u32(PCP_PROC_MEM_LIBRS, pid, offset, 0);
   pp->m_drs = Metric_instance_u32(PCP_PROC_MEM_DATRS, pid, offset, 0);
   pp->m_dt = Metric_instance_u32(PCP_PROC_MEM_DIRTY, pid, offset, 0);
}

function: PCPProcessTable_updateProcesses

function: PCPProcessTable_updateSmaps

static void PCPProcessTable_updateSmaps(PCPProcess* pp, pid_t pid, int offset) {
   pp->m_pss = Metric_instance_u64(PCP_PROC_SMAPS_PSS, pid, offset, 0);
   pp->m_swap = Metric_instance_u64(PCP_PROC_SMAPS_SWAP, pid, offset, 0);
   pp->m_psswp = Metric_instance_u64(PCP_PROC_SMAPS_SWAPPSS, pid, offset, 0);
}

function: PCPProcessTable_updateTTY

static void PCPProcessTable_updateTTY(Process* process, int pid, int offset) {
   process->tty_name = setString(PCP_PROC_TTYNAME, pid, offset, process->tty_name);
}

function: PCPProcessTable_updateUsername

static void PCPProcessTable_updateUsername(Process* process, int pid, int offset, UsersTable* users) {
   process->st_uid = Metric_instance_u32(PCP_PROC_ID_UID, pid, offset, 0);
   process->user = setUser(users, process->st_uid, pid, offset);
}

function: ProcessTable_delete

void ProcessTable_delete(Object* cast) {
   PCPProcessTable* this = (PCPProcessTable*) cast;
   ProcessTable_done(&this->super);
   free(this);
}

function: ProcessTable_goThroughEntries

void ProcessTable_goThroughEntries(ProcessTable* super) {
   PCPProcessTable* this = (PCPProcessTable*) super;
   PCPProcessTable_updateProcesses(this);
}

function: ProcessTable_new

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
   PCPProcessTable* this = xCalloc(1, sizeof(PCPProcessTable));
   Object_setClass(this, Class(ProcessTable));

   ProcessTable* super = &this->super;
   ProcessTable_init(super, Class(PCPProcess), host, pidMatchList);

   return super;
}

function: setString

static char* setString(Metric metric, int pid, int offset, char* string) {
   if (string)
      free(string);
   pmAtomValue value;
   if (Metric_instance(metric, pid, offset, &value, PM_TYPE_STRING))
      string = value.cp;
   else
      string = NULL;
   return string;
}

function: setUser

static char* setUser(UsersTable* this, unsigned int uid, int pid, int offset) {
   char* name = Hashtable_get(this->users, uid);
   if (name)
      return name;

   pmAtomValue value;
   if (Metric_instance(PCP_PROC_ID_USER, pid, offset, &value, PM_TYPE_STRING)) {
      Hashtable_put(this->users, uid, value.cp);
      name = value.cp;
   }
   return name;
}

struct: PCPProcessTable

// The full definition of struct PCPProcessTable is not present in this file.
// It is declared in 'pcp/PCPProcessTable.h' and typically includes:
// struct ProcessTable super; // Base ProcessTable structure
// ... other PCP-specific members ...

PCPProcessTable.h

pcp/PCPProcessTable.h

struct: PCPProcessTable

typedef struct PCPProcessTable_ {
   ProcessTable super;
} PCPProcessTable;

Platform.c

pcp/Platform.c

file: Platform.c

Platform.h

pcp/Platform.h

enum: PLATFORM_LONGOPT_enum

enum {
   PLATFORM_LONGOPT_HOST = 128,
   PLATFORM_LONGOPT_TIMEZONE,
   PLATFORM_LONGOPT_HOSTZONE,
};

function: Platform_addDynamicScreen

void Platform_addDynamicScreen(ScreenSettings* ss);

function: Platform_addDynamicScreenAvailableColumns

void Platform_addDynamicScreenAvailableColumns(Panel* availableColumns, const char* screen);

function: Platform_addMetric

size_t Platform_addMetric(Metric id, const char* name);

function: Platform_defaultDynamicScreens

void Platform_defaultDynamicScreens(Settings* settings);

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

const char* Platform_dynamicColumnName(unsigned int key);

function: Platform_dynamicColumns

Hashtable* Platform_dynamicColumns(void);

function: Platform_dynamicColumnsDone

void Platform_dynamicColumnsDone(Hashtable* columns);

function: Platform_dynamicColumnWriteField

bool Platform_dynamicColumnWriteField(const Process* proc, RichString* str, unsigned int key);

function: Platform_dynamicMeterDisplay

void Platform_dynamicMeterDisplay(const Meter* meter, RichString* out);

function: Platform_dynamicMeterInit

void Platform_dynamicMeterInit(Meter* meter);

function: Platform_dynamicMeters

Hashtable* Platform_dynamicMeters(void);

function: Platform_dynamicMetersDone

void Platform_dynamicMetersDone(Hashtable* meters);

function: Platform_dynamicMeterUpdateValues

void Platform_dynamicMeterUpdateValues(Meter* meter);

function: Platform_dynamicScreens

Hashtable* Platform_dynamicScreens(void);

function: Platform_dynamicScreensDone

void Platform_dynamicScreensDone(Hashtable* screens);

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getBootTime

long long Platform_getBootTime(void);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

void Platform_getHostname(char* buffer, size_t size);

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv);

function: Platform_getMaxCPU

unsigned int Platform_getMaxCPU(void);

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getPressureStall

void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

void Platform_getRelease(char** string);

function: Platform_gettime_monotonic

void Platform_gettime_monotonic(uint64_t* msec);

function: Platform_gettime_realtime

void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec);

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

void Platform_longOptionsUsage(const char* name);

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this);

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this);

function: Platform_setZramValues

void Platform_setZramValues(Meter* this);

function: Platform_updateTables

void Platform_updateTables(Machine* host);

macro: PLATFORM_LONG_OPTIONS

#define PLATFORM_LONG_OPTIONS \
      {PMLONGOPT_HOST, optional_argument, 0, PLATFORM_LONGOPT_HOST}, \
      {PMLONGOPT_TIMEZONE, optional_argument, 0, PLATFORM_LONGOPT_TIMEZONE}, \
      {PMLONGOPT_HOSTZONE, optional_argument, 0, PLATFORM_LONGOPT_HOSTZONE}, \

struct: Platform

typedef struct Platform_ {
   int context;               /* PMAPI(3) context identifier */
   size_t totalMetrics;       /* total number of all metrics */
   const char** names;        /* name array indexed by Metric */
   pmID* pmids;               /* all known metric identifiers */
   pmID* fetch;               /* enabled identifiers for sampling */
   pmDesc* descs;             /* metric desc array indexed by Metric */
   pmResult* result;          /* sample values result indexed by Metric */
   PCPDynamicMeters meters;   /* dynamic meters via configuration files */
   PCPDynamicColumns columns; /* dynamic columns via configuration files */
   PCPDynamicScreens screens; /* dynamic screens via configuration files */
   struct timeval offset;     /* time offset used in archive mode only */
   long long btime;           /* boottime in seconds since the epoch */
   char* release;             /* uname and distro from this context */
   int pidmax;                /* maximum platform process identifier */
   unsigned int ncpu;         /* maximum processor count configured */
} Platform;

variable: opts

extern pmOptions opts;

variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

variable: Platform_signals

extern const SignalItem Platform_signals[];

ProcessField.h

pcp/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   CMINFLT = 11,                 \
   CMAJFLT = 13,                 \
   UTIME = 14,                   \
   STIME = 15,                   \
   CUTIME = 16,                  \
   CSTIME = 17,                  \
   M_SHARE = 41,                 \
   M_TRS = 42,                   \
   M_DRS = 43,                   \
   M_LRS = 44,                   \
   M_DT = 45,                    \
   CTID = 100,                   \
   RCHAR = 103,                  \
   WCHAR = 104,                  \
   SYSCR = 105,                  \
   SYSCW = 106,                  \
   RBYTES = 107,                 \
   WBYTES = 108,                 \
   CNCLWB = 109,                 \
   IO_READ_RATE = 110,           \
   IO_WRITE_RATE = 111,          \
   IO_RATE = 112,                \
   CGROUP = 113,                 \
   OOM = 114,                    \
   PERCENT_CPU_DELAY = 116,      \
   PERCENT_IO_DELAY = 117,       \
   PERCENT_SWAP_DELAY = 118,     \
   M_PSS = 119,                  \
   M_SWAP = 120,                 \
   M_PSSWP = 121,                \
   CTXT = 122,                   \
   SECATTR = 123,                \
   AUTOGROUP_ID = 127,           \
   AUTOGROUP_NICE = 128,         \
   CCGROUP = 129,                \
   CONTAINER = 130,              \
   M_PRIV = 131,                 \
   // End of list

biosnoop

pcp/screens/biosnoop

file: biosnoop

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[biosnoop]
heading = BioSnoop
caption = BPF block I/O snoop
default = false

pid.heading = PID
pid.caption = Process identifier
pid.metric = bpf.biosnoop.pid
pid.format = process

disk.heading = DISK
disk.caption = Device name
disk.width = -7
disk.metric = bpf.biosnoop.disk

rwbs.heading = TYPE
rwbs.caption = I/O type string
rwbs.width = -4
rwbs.metric = bpf.biosnoop.rwbs

bytes.heading = BYTES
bytes.caption = I/O size in bytes
bytes.metric = bpf.biosnoop.bytes

lat.heading = LAT
lat.caption = I/O latency
lat.metric = bpf.biosnoop.lat

sector.heading = SECTOR
sector.caption = Device sector
sector.metric = bpf.biosnoop.sector

comm.heading = Command
comm.caption = Process command name
comm.width = -16
comm.metric = bpf.biosnoop.comm
comm.format = process

cgroups

pcp/screens/cgroups

file: cgroups

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[cgroups]
heading = CGroups
caption = Control Groups
default = true

user_cpu.heading = UTIME
user_cpu.caption = User CPU Time
user_cpu.metric = 1000 * rate(cgroup.cpu.stat.user)
user_cpu.width = 6

system_cpu.heading = STIME
system_cpu.caption = Kernel CPU Time
system_cpu.metric = 1000 * rate(cgroup.cpu.stat.system)
system_cpu.width = 6

cpu_usage.heading = CPU%
cpu_usage.caption = CPU Utilization
cpu_usage.metric = 100 * (rate(cgroup.cpu.stat.usage) / hinv.ncpu)
cpu_usage.format = percent

cpu_psi.heading = CPU-PSI
cpu_psi.caption = CPU Pressure Stall Information
cpu_psi.metric = 1000 * rate(cgroup.pressure.cpu.some.total)
cpu_psi.width = 7

mem_psi.heading = MEM-PSI
mem_psi.caption = Memory Pressure Stall Information
mem_psi.metric = 1000 * rate(cgroup.pressure.memory.some.total)
mem_psi.width = 7

io_psi.heading = I/O-PSI
io_psi.caption = I/O Pressure Stall Information
io_psi.metric = 1000 * rate(cgroup.pressure.io.some.total)
io_psi.width = 7

name.heading = Control group
name.caption = Control group name
name.width = -64
name.metric = cgroup.cpu.stat.system
name.instances = true
name.format = cgroup

cgroupsio

pcp/screens/cgroupsio

file: cgroupsio

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[cgroupsio]
heading = CGroupsIO
caption = Control Groups I/O
default = false

iops.heading = IOPS
iops.caption = I/O operations
iops.metric = rate(cgroup.io.stat.rios) + rate(cgroup.io.stat.wios) + rate(cgroup.io.stat.dios)

readops.heading = RDIO
readops.caption = Read operations
readops.metric = rate(cgroup.io.stat.rios)
readops.default = false

writeops.heading = WRIO
writeops.caption = Write operations
writeops.metric = rate(cgroup.io.stat.wios)
writeops.default = false

directops.heading = DIO
directops.caption = Direct I/O operations
directops.metric = rate(cgroup.io.stat.dios)
directops.default = false

totalbytes.heading = R/W/D
totalbytes.caption = Disk throughput
totalbytes.metric = rate(cgroup.io.stat.rbytes) + rate(cgroup.io.stat.wbytes) + rate(cgroup.io.stat.dbytes)

readbytes.heading = RBYTE
readbytes.caption = Disk read throughput
readbytes.metric = rate(cgroup.io.stat.rbytes)

writebytes.heading = WBYTE
writebytes.caption = Disk throughput
writebytes.metric = rate(cgroup.io.stat.wbytes)

directio.heading = DBYTE
directio.caption = Direct I/O throughput
directio.metric = rate(cgroup.io.stat.dbytes)

name.heading = Control group device
name.caption = Control group device
name.width = -64
name.metric = cgroup.io.stat.rbytes
name.instances = true

cgroupsmem

pcp/screens/cgroupsmem

file: cgroupsmem

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[cgroupsmem]
heading = CGroupsMem
caption = Control Groups Memory
default = false

current.heading = MEM
current.caption = Current memory
current.metric = cgroup.memory.current

usage.heading = USAGE
usage.caption = Memory usage
usage.metric = cgroup.memory.usage

container.heading = CONTAINER
container.caption = Container Name
container.metric = cgroup.memory.id.container

resident.heading = RSS
resident.metric = cgroup.memory.stat.rss

cresident.heading = CRSS
cresident.metric = cgroup.memory.stat.total.rss

anonmem.heading = ANON
anonmem.metric = cgroup.memory.stat.anon

filemem.heading = FILE
filemem.metric = cgroup.memory.stat.file

shared.heading = SHMEM
shared.metric = cgroup.memory.stat.shmem

swap.heading = SWAP
swap.metric = cgroup.memory.stat.swap

pgfault.heading = FAULTS
pgfault.metric = cgroup.memory.stat.pgfaults

name.heading = Control group
name.caption = Control group name
name.width = -64
name.metric = cgroup.memory.current
name.instances = true
name.format = cgroup

devices

pcp/screens/devices

file: devices

execsnoop

pcp/screens/execsnoop

file: execsnoop

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[execsnoop]
heading = ExecSnoop
caption = BPF exec(2) syscall snoop
default = false

pid.heading = PID
pid.caption = Process Identifier
pid.metric = bpf.execsnoop.pid
pid.format = process

ppid.heading = PPID
ppid.caption = Parent Process
ppid.metric = bpf.execsnoop.ppid
ppid.format = process

uid.heading = UID
uid.caption = User Identifier
uid.metric = bpf.execsnoop.uid

comm.heading = COMM
comm.caption = Command
comm.width = -16
comm.metric = bpf.execsnoop.comm
comm.format = command

ret.heading = RET
ret.caption = Return Code
ret.metric = bpf.execsnoop.ret

path.heading = Arguments
path.caption = Arguments
path.width = -12
path.metric = bpf.execsnoop.args

exitsnoop

pcp/screens/exitsnoop

file: exitsnoop

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[exitsnoop]
heading = ExitSnoop
caption = BPF process exit(2) snoop
default = false

pid.heading = PID
pid.caption = Process Identifier
pid.metric = bpf.exitsnoop.pid
pid.format = process

ppid.heading = PPID
ppid.caption = Parent Process
ppid.metric = bpf.exitsnoop.ppid
ppid.format = process

tid.heading = TID
tid.caption = Task Identifier
tid.metric = bpf.exitsnoop.tid
tid.format = process
tid.default = false

signal.heading = SIG
signal.caption = Signal number
signal.metric = bpf.exitsnoop.sig

exit.heading = EXIT
exit.caption = Exit Code
exit.metric = bpf.exitsnoop.exit_code

core.heading = CORE
core.caption = Dumped core
core.metric = bpf.exitsnoop.coredump
core.default = false

age.heading = AGE
age.caption = Process age
age.metric = bpf.exitsnoop.age
age.default = false

comm.heading = Command
comm.caption = COMM
comm.width = -16
comm.metric = bpf.exitsnoop.comm
comm.format = command

filesystems

pcp/screens/filesystems

file: filesystems

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[filesystems]
heading = Filesystems
caption = Mounted block device filesystems

blockdev.heading = Device
blockdev.metric = filesys.mountdir
blockdev.instances = true
blockdev.width = -14

blocksize.heading = BSIZE
blocksize.metric = filesys.blocksize
blocksize.default = false

capacity.heading = SIZE
capacity.metric = filesys.capacity

used.heading = USED
used.metric = filesys.used

free.heading = FREE
free.metric = filesys.free
free.default = false

avail.heading = AVAIL
avail.metric = filesys.avail

full.heading = USE%
full.metric = filesys.full
full.format = percent

usedfiles.heading = USEDF
usedfiles.metric = filesys.usedfiles
usedfiles.default = false

freefiles.heading = FREEF
freefiles.metric = filesys.freefiles
freefiles.default = false

maxfiles.heading = MAXF
maxfiles.metric = filesys.maxfiles
maxfiles.default = false

mountdir.heading = Mount point
mountdir.metric = filesys.mountdir
mountdir.format = path
mountdir.width = -33

opensnoop

pcp/screens/opensnoop

file: opensnoop

#
# pcp-htop(1) configuration file - see pcp-htop(5)
#

[opensnoop]
heading = OpenSnoop
caption = BPF open(2) syscall snoop
default = false

pid.heading = PID
pid.metric = bpf.opensnoop.pid
pid.format = process

comm.heading = COMM
comm.metric = bpf.opensnoop.comm
comm.format = command

fd.heading = FD
fd.metric = bpf.opensnoop.fd

err.heading = ERR
err.metric = bpf.opensnoop.err

file.heading = File name
file.width = -32
file.metric = bpf.opensnoop.fname
file.format = path

Process.c

Process.c

file: Process.c

Process.h

Process.h

enum: ProcessState

typedef enum ProcessState_ {
   UNKNOWN = 1,
   RUNNABLE,
   RUNNING,
   QUEUED,
   WAITING,
   UNINTERRUPTIBLE_WAIT,
   BLOCKED,
   PAGING,
   STOPPED,
   TRACED,
   ZOMBIE,
   DEFUNCT,
   IDLE,
   SLEEPING
} ProcessState;

enum: Tristate

typedef enum Tristate_ {
   TRI_INITIAL = 0,
   TRI_OFF = -1,
   TRI_ON = 1,
} Tristate;

function: Process_compare

int Process_compare(const void* v1, const void* v2);

function: Process_compareByKey_Base

int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);

function: Process_compareByParent

int Process_compareByParent(const Row* r1, const Row* r2);

function: Process_delete

void Process_delete(Object* cast);

function: Process_done

void Process_done(Process* this);

function: Process_fillStarttimeBuffer

void Process_fillStarttimeBuffer(Process* this);

function: Process_getCommand

const char* Process_getCommand(const Process* this);

function: Process_getGroupOrParent

static inline pid_t Process_getGroupOrParent(const Process* this) {
   return Row_getGroupOrParent(&this->super);
}

function: Process_getParent

static inline pid_t Process_getParent(const Process* this) {
   return (pid_t)this->super.parent;
}

function: Process_getPid

static inline pid_t Process_getPid(const Process* this) {
   return (pid_t)this->super.id;
}

function: Process_getThreadGroup

static inline pid_t Process_getThreadGroup(const Process* this) {
   return (pid_t)this->super.group;
}

function: Process_init

void Process_init(Process* this, const struct Machine_* host);

function: Process_isKernelThread

static inline bool Process_isKernelThread(const Process* this) {
   return this->isKernelThread;
}

function: Process_isThread

static inline bool Process_isThread(const Process* this) {
   return Process_isUserlandThread(this) || Process_isKernelThread(this);
}

function: Process_isUserlandThread

static inline bool Process_isUserlandThread(const Process* this) {
   return this->isUserlandThread;
}

function: Process_makeCommandStr

void Process_makeCommandStr(Process* this, const struct Settings_ *settings);

function: Process_pidEqualCompare

static inline int Process_pidEqualCompare(const void* v1, const void* v2) {
   return Row_idEqualCompare(v1, v2);
}

function: Process_rowChangePriorityBy

bool Process_rowChangePriorityBy(Row* super, Arg delta);

function: Process_rowGetSortKey

const char* Process_rowGetSortKey(Row* super);

function: Process_rowIsHighlighted

bool Process_rowIsHighlighted(const Row* super);

function: Process_rowIsVisible

bool Process_rowIsVisible(const Row* super, const struct Table_* table);

function: Process_rowMatchesFilter

bool Process_rowMatchesFilter(const Row* super, const struct Table_* table);

function: Process_rowSendSignal

bool Process_rowSendSignal(Row* super, Arg sgn);

function: Process_setParent

static inline void Process_setParent(Process* this, pid_t pid) {
   this->super.parent = pid;
}

function: Process_setPid

static inline void Process_setPid(Process* this, pid_t pid) {
   this->super.id = pid;
}

function: Process_setThreadGroup

static inline void Process_setThreadGroup(Process* this, pid_t pid) {
   this->super.group = pid;
}

function: Process_updateCmdline

void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd);

function: Process_updateComm

void Process_updateComm(Process* this, const char* comm);

function: Process_updateCPUFieldWidths

void Process_updateCPUFieldWidths(float percentage);

function: Process_updateExe

void Process_updateExe(Process* this, const char* exe);

function: Process_writeCommand

void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str);

function: Process_writeField

void Process_writeField(const Process* this, RichString* str, ProcessField field);

global_variable: Process_class

extern const ProcessClass Process_class;

global_variable: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

macro: As_Process

#define As_Process(this_)   ((const ProcessClass*)((this_)->super.super.klass))

macro: CMDLINE_HIGHLIGHT_FLAG_BASENAME

#define CMDLINE_HIGHLIGHT_FLAG_BASENAME   0x00000002

macro: CMDLINE_HIGHLIGHT_FLAG_COMM

#define CMDLINE_HIGHLIGHT_FLAG_COMM       0x00000004

macro: CMDLINE_HIGHLIGHT_FLAG_DELETED

#define CMDLINE_HIGHLIGHT_FLAG_DELETED    0x00000008

macro: CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR

#define CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR  0x00000010

macro: CMDLINE_HIGHLIGHT_FLAG_SEPARATOR

#define CMDLINE_HIGHLIGHT_FLAG_SEPARATOR  0x00000001

macro: DEFAULT_HIGHLIGHT_SECS

#define DEFAULT_HIGHLIGHT_SECS 5

macro: LAST_PROCESSFIELD

#define LAST_PROCESSFIELD LAST_RESERVED_FIELD

macro: Process_compareByKey

#define Process_compareByKey(p1_, p2_, key_)   (As_Process(p1_)->compareByKey ? (As_Process(p1_)->compareByKey(p1_, p2_, key_)) : Process_compareByKey_Base(p1_, p2_, key_))

macro: PROCESS_FLAG_CWD

#define PROCESS_FLAG_CWD             0x00000002

macro: PROCESS_FLAG_IO

#define PROCESS_FLAG_IO              0x00000001

macro: PROCESS_FLAG_SCHEDPOL

#define PROCESS_FLAG_SCHEDPOL        0x00000004

macro: PROCESS_NICE_UNKNOWN

#define PROCESS_NICE_UNKNOWN (-LONG_MAX)

macro: Process_pidDigits

#define Process_pidDigits Row_pidDigits

macro: Process_uidDigits

#define Process_uidDigits Row_uidDigits

struct: Machine_

struct Machine_;

struct: Process

struct: ProcessClass

typedef struct ProcessClass_ {
   const RowClass super;
   const Process_CompareByKey compareByKey;
} ProcessClass;

struct: ProcessCmdlineHighlight

typedef struct ProcessCmdlineHighlight_ {
   size_t offset; /* first character to highlight */
   size_t length; /* How many characters to highlight, zero if unused */
   int attr;      /* The attributes used to highlight */
   int flags;     /* Special flags used for selective highlighting, zero for always */
} ProcessCmdlineHighlight;

struct: ProcessFieldData

typedef struct ProcessFieldData_ {
   /* Name (displayed in setup menu) */
   const char* name;

   /* Title (display in main screen); must have same width as the printed values */
   const char* title;

   /* Description (displayed in setup menu) */
   const char* description;

   /* Scan flag to enable scan-method otherwise not run */
   uint32_t flags;

   /* Whether the values are process identifiers; adjusts the width of title and values if true */
   bool pidColumn;

   /* Whether the column should be sorted in descending order by default */
   bool defaultSortDesc;

   /* Whether the column width is dynamically adjusted (the minimum width is determined by the title length) */
   bool autoWidth;

   /* Whether the title of a column with dynamically adjusted width is right aligned (default is left aligned) */
   bool autoTitleRightAlign;
} ProcessFieldData;

struct: ProcessMergedCommand

typedef struct ProcessMergedCommand_ {
   uint64_t lastUpdate;                        /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */
   char* str;                                  /* merged Command string */
   size_t highlightCount;                      /* how many portions of cmdline to highlight */
   ProcessCmdlineHighlight highlights[8];      /* which portions of cmdline to highlight */
} ProcessMergedCommand;

struct: Settings_

struct Settings_;

typedef: Process_CompareByKey

typedef int (*Process_CompareByKey)(const Process*, const Process*, ProcessField);

typedef: Process_New

typedef Process* (*Process_New)(const struct Machine_*);

typedef: ProcessField

typedef int32_t ProcessField;

ProcessLocksScreen.c

ProcessLocksScreen.c

function: FileLocks_Data_clear

static inline void FileLocks_Data_clear(FileLocks_Data* data) {
   free(data->locktype);
   free(data->exclusive);
   free(data->readwrite);
   free(data->filename);
}

function: ProcessLocksScreen_delete

void ProcessLocksScreen_delete(Object* this) {
   free(InfoScreen_done((InfoScreen*)this));
}

function: ProcessLocksScreen_draw

static void ProcessLocksScreen_draw(InfoScreen* this) {
   InfoScreen_drawTitled(this, "Snapshot of file locks of process %d - %s", ((ProcessLocksScreen*)this)->pid, Process_getCommand(this->process));
}

function: ProcessLocksScreen_new

ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) {
   ProcessLocksScreen* this = xMalloc(sizeof(ProcessLocksScreen));
   Object_setClass(this, Class(ProcessLocksScreen));
   if (Process_isThread(process))
      this->pid = Process_getThreadGroup(process);
   else
      this->pid = Process_getPid(process);

   return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, "   FD TYPE       EXCLUSION  READ/WRITE DEVICE       NODE               START                 END  FILENAME");
}

function: ProcessLocksScreen_scan

static void ProcessLocksScreen_scan(InfoScreen* this) {
   Panel* panel = this->display;
   int idx = Panel_getSelectedIndex(panel);
   Panel_prune(panel);
   FileLocks_ProcessData* pdata = Platform_getProcessLocks(((ProcessLocksScreen*)this)->pid);
   if (!pdata) {
      InfoScreen_addLine(this, "This feature is not supported on your platform.");
   } else if (pdata->error) {
      InfoScreen_addLine(this, "Could not determine file locks.");
   } else {
      FileLocks_LockData* ldata = pdata->locks;
      if (!ldata) {
         InfoScreen_addLine(this, "No locks have been found for the selected process.");
      }
      while (ldata) {
         FileLocks_Data* data = &ldata->data;

         char entry[512];
         if (ULLONG_MAX == data->end) {
            xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %#6"PRIx64" %10"PRIu64" %19"PRIu64" %19s  %s",
               data->fd,
               data->locktype, data->exclusive, data->readwrite,
               (uint64_t) data->dev, data->inode,
               data->start, "<END OF FILE>",
               data->filename ? data->filename : "<N/A>"
            );
         } else {
            xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %#6"PRIx64" %10"PRIu64" %19"PRIu64" %19"PRIu64"  %s",
               data->fd,
               data->locktype, data->exclusive, data->readwrite,
               (uint64_t) data->dev, data->inode,
               data->start, data->end,
               data->filename ? data->filename : "<N/A>"
            );
         }

         InfoScreen_addLine(this, entry);
         FileLocks_Data_clear(&ldata->data);

         FileLocks_LockData* old = ldata;
         ldata = ldata->next;
         free(old);
      }
   }
   free(pdata);
   Vector_insertionSort(this->lines);
   Vector_insertionSort(panel->items);
   Panel_setSelected(panel, idx);
}

struct: ProcessLocksScreen_class

const InfoScreenClass ProcessLocksScreen_class = {
   .super = {
      .extends = Class(Object),
      .delete = ProcessLocksScreen_delete
   },
   .scan = ProcessLocksScreen_scan,
   .draw = ProcessLocksScreen_draw
};

ProcessLocksScreen.h

ProcessLocksScreen.h

class: ProcessLocksScreen

typedef struct ProcessLocksScreen_ {
   InfoScreen super;
   pid_t pid;
} ProcessLocksScreen;

function: ProcessLocksScreen_delete

void ProcessLocksScreen_delete(Object* this);

function: ProcessLocksScreen_new

ProcessLocksScreen* ProcessLocksScreen_new(const Process* process);

global_variable: ProcessLocksScreen_class

extern const InfoScreenClass ProcessLocksScreen_class;

struct: FileLocks_Data

typedef struct FileLocks_Data_ {
   char* locktype;
   char* exclusive;
   char* readwrite;
   char* filename;
   int fd;
   dev_t dev;
   uint64_t inode;
   uint64_t start;
   uint64_t end;
} FileLocks_Data;

struct: FileLocks_LockData

typedef struct FileLocks_LockData_ {
   FileLocks_Data data;
   struct FileLocks_LockData_* next;
} FileLocks_LockData;

struct: FileLocks_ProcessData

typedef struct FileLocks_ProcessData_ {
   bool error;
   struct FileLocks_LockData_* locks;
} FileLocks_ProcessData;

ProcessTable.c

ProcessTable.c

function: ProcessTable_cleanupEntries

static void ProcessTable_cleanupEntries(Table* super) {
   Machine* host = super->host;
   const Settings* settings = host->settings;

   // Finish process table update, culling any exit'd processes
   for (int i = Vector_size(super->rows) - 1; i >= 0; i--) {
      Process* p = (Process*) Vector_get(super->rows, i);

      // tidy up Process state after refreshing the ProcessTable table
      Process_makeCommandStr(p, settings);

      // keep track of the highest UID for column scaling
      if (p->st_uid > host->maxUserId)
         host->maxUserId = p->st_uid;

      Table_cleanupRow(super, (Row*) p, i);
   }

   // compact the table in case of deletions
   Table_compact(super);
}

function: ProcessTable_done

void ProcessTable_done(ProcessTable* this) {
   Table_done(&this->super);
}

function: ProcessTable_getProcess

Process* ProcessTable_getProcess(ProcessTable* this, pid_t pid, bool* preExisting, Process_New constructor) {
   const Table* table = &this->super;
   Process* proc = (Process*) Hashtable_get(table->table, pid);
   *preExisting = proc != NULL;
   if (proc) {
      assert(Vector_indexOf(table->rows, proc, Row_idEqualCompare) != -1);
      assert(Process_getPid(proc) == pid);
   } else {
      proc = constructor(table->host);
      assert(proc->cmdline == NULL);
      Process_setPid(proc, pid);
   }
   return proc;
}

function: ProcessTable_init

void ProcessTable_init(ProcessTable* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList) {
   Table_init(&this->super, klass, host);

   this->pidMatchList = pidMatchList;
}

function: ProcessTable_iterateEntries

static void ProcessTable_iterateEntries(Table* super) {
   ProcessTable* this = (ProcessTable*) super;
   // calling into platform-specific code
   ProcessTable_goThroughEntries(this);
}

function: ProcessTable_prepareEntries

static void ProcessTable_prepareEntries(Table* super) {
   ProcessTable* this = (ProcessTable*) super;
   this->totalTasks = 0;
   this->userlandThreads = 0;
   this->kernelThreads = 0;
   this->runningTasks = 0;

   Table_prepareEntries(super);
}

struct: ProcessTable_class

const TableClass ProcessTable_class = {
   .super = {
      .extends = Class(Table),
      .delete = ProcessTable_delete,
   },
   .prepare = ProcessTable_prepareEntries,
   .iterate = ProcessTable_iterateEntries,
   .cleanup = ProcessTable_cleanupEntries,
};

ProcessTable.h

ProcessTable.h

function: ProcessTable_add

static inline void ProcessTable_add(ProcessTable* this, Process* process) {
   Table_add(&this->super, &process->super);
}

function: ProcessTable_delete

void ProcessTable_delete(Object* cast);

function: ProcessTable_done

void ProcessTable_done(ProcessTable* this);

function: ProcessTable_findProcess

static inline Process* ProcessTable_findProcess(ProcessTable* this, pid_t pid) {
   return (Process*) Table_findRow(&this->super, pid);
}

function: ProcessTable_getProcess

Process* ProcessTable_getProcess(ProcessTable* this, pid_t pid, bool* preExisting, Process_New constructor);

function: ProcessTable_goThroughEntries

void ProcessTable_goThroughEntries(ProcessTable* this);

function: ProcessTable_init

void ProcessTable_init(ProcessTable* this, const ObjectClass* klass, Machine* host, Hashtable* pidMatchList);

function: ProcessTable_new

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList);

struct: ProcessTable_

typedef struct ProcessTable_ {
   Table super;

   Hashtable* pidMatchList;

   unsigned int totalTasks;
   unsigned int runningTasks;
   unsigned int userlandThreads;
   unsigned int kernelThreads;
} ProcessTable;

variable: ProcessTable_class

extern const TableClass ProcessTable_class;

ProvideCurses.h

ProvideCurses.h

file: ProvideCurses.h

#ifndef HEADER_ProvideCurses
#define HEADER_ProvideCurses
/*
htop - ProvideCurses.h
(C) 2004,2011 Hisham H. Muhammad
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/


// This header is also used in tests by configure, thus conditionally
// including "config.h".
#if defined(HAVE_CONFIG_H)
#include "config.h" // IWYU pragma: keep
#endif

// IWYU pragma: begin_exports

#if defined(HAVE_NCURSESW_CURSES_H)
#include <ncursesw/curses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
#include <ncurses/ncurses.h>
#elif defined(HAVE_NCURSES_CURSES_H)
#include <ncurses/curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#elif defined(HAVE_CURSES_H)
#include <curses.h>
#endif

#ifdef HAVE_LIBNCURSESW
#include <wchar.h>
#include <wctype.h>
#endif

// IWYU pragma: end_exports

#endif // HEADER_ProvideCurses

ProvideTerm.h

ProvideTerm.h

file: ProvideTerm.h

#ifndef HEADER_ProvideTerm
#define HEADER_ProvideTerm
/*
htop - ProvideTerm.h
(C) 2023 htop dev team
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include "config.h" // IWYU pragma: keep

// IWYU pragma: begin_exports

#if defined(HAVE_NCURSESW_TERM_H)
#include <ncursesw/term.h>
#elif defined(HAVE_NCURSES_TERM_H)
#include <ncurses/term.h>
#elif defined(HAVE_TERM_H)
#include <term.h>
#endif

// IWYU pragma: end_exports

#endif // HEADER_ProvideTerm

README

README

file: README

README.md

README.md

README.md

function: compile_htop_from_source

./autogen.sh && ./configure && make

function: configure_htop_installation_prefix

./configure --prefix=/some/path

function: install_archlinux_manjaro_build_dependencies

sudo pacman -S ncurses automake autoconf gcc

function: install_debian_ubuntu_build_dependencies

sudo apt install libncursesw5-dev autotools-dev autoconf automake build-essential

function: install_fedora_rhel_build_dependencies

sudo dnf install ncurses-devel automake autoconf gcc

function: install_htop_locally

make install

function: install_macos_build_dependencies

brew install ncurses automake autoconf gcc

function: view_htop_manual_page

man htop

method: get_htop_in_app_help

F1 or h inside htop

RichString.c

RichString.c

function: mbstowcs_nonfatal

static size_t mbstowcs_nonfatal(wchar_t* restrict dest, const char* restrict src, size_t n) {
   size_t written = 0;
   mbstate_t ps = { 0 };
   bool broken = false;

   while (n > 0) {
      size_t ret = mbrtowc(dest, src, n, &ps);
      if (ret == (size_t)-1 || ret == (size_t)-2) {
         if (!broken) {
            broken = true;
            *dest++ = L'\xFFFD';
            written++;
         }
         src++;
         n--;
         continue;
      }

      broken = false;

      if (ret == 0) {
         break;
      }

      dest++;
      written++;
      src += ret;
      n -= ret;
   }

   return written;
}

function: RichString_appendAscii

int RichString_appendAscii(RichString* this, int attrs, const char* data) {
   return RichString_writeFromAscii(this, attrs, data, this->chlen, strlen(data));
}

function: RichString_appendChr (!HAVE_LIBNCURSESW)

void RichString_appendChr(RichString* this, int attrs, char c, int count) {
   int from = this->chlen;
   int newLen = from + count;
   RichString_setLen(this, newLen);
   for (int i = from; i < newLen; i++) {
      this->chptr[i] = c | attrs;
   }
}

function: RichString_appendChr (HAVE_LIBNCURSESW)

void RichString_appendChr(RichString* this, int attrs, char c, int count) {
   int from = this->chlen;
   int newLen = from + count;
   RichString_setLen(this, newLen);
   for (int i = from; i < newLen; i++) {
      this->chptr[i] = (CharType) { .attr = attrs, .chars = { c, 0 } };
   }
}

function: RichString_appendnAscii

int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len) {
   return RichString_writeFromAscii(this, attrs, data, this->chlen, len);
}

function: RichString_appendnWide

int RichString_appendnWide(RichString* this, int attrs, const char* data, int len) {
   return RichString_writeFromWide(this, attrs, data, this->chlen, len);
}

function: RichString_appendnWideColumns (!HAVE_LIBNCURSESW)

int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
   int written = RichString_writeFromWide(this, attrs, data_c, this->chlen, MINIMUM(len, *columns));
   *columns = written;
   return written;
}

function: RichString_appendnWideColumns (HAVE_LIBNCURSESW)

int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_c, int len, int* columns) {
   wchar_t data[len];
   len = mbstowcs_nonfatal(data, data_c, len);
   if (len <= 0)
      return 0;

   int from = this->chlen;
   int newLen = from + len;
   RichString_setLen(this, newLen);
   int columnsWritten = 0;
   int pos = from;
   for (int j = 0; j < len; j++) {
      wchar_t c = iswprint(data[j]) ? data[j] : L'\xFFFD';
      int cwidth = wcwidth(c);
      if (cwidth > *columns)
         break;

      *columns -= cwidth;
      columnsWritten += cwidth;

      this->chptr[pos] = (CharType) { .attr = attrs & 0xffffff, .chars = { c, '\0' } };
      pos++;
   }

   RichString_setLen(this, pos);
   *columns = columnsWritten;

   return pos - from;
}

function: RichString_appendWide

int RichString_appendWide(RichString* this, int attrs, const char* data) {
   return RichString_writeFromWide(this, attrs, data, this->chlen, strlen(data));
}

function: RichString_delete

void RichString_delete(RichString* this) {
   if (this->chlen > RICHSTRING_MAXLEN) {
      free(this->chptr);
      this->chptr = this->chstr;
   }
}

function: RichString_extendLen

static void RichString_extendLen(RichString* this, int len) {
   if (this->chptr == this->chstr) {
      // String is in internal buffer
      if (len > RICHSTRING_MAXLEN) {
         // Copy from internal buffer to allocated string
         this->chptr = xMalloc(charBytes(len + 1));
         memcpy(this->chptr, this->chstr, charBytes(this->chlen));
      } else {
         // Still fits in internal buffer, do nothing
         assert(this->chlen <= RICHSTRING_MAXLEN);
      }
   } else {
      // String is managed externally
      if (len > RICHSTRING_MAXLEN) {
         // Just reallocate the buffer accordingly
         this->chptr = xRealloc(this->chptr, charBytes(len + 1));
      } else {
         // Move string into internal buffer and free resources
         memcpy(this->chstr, this->chptr, charBytes(len));
         free(this->chptr);
         this->chptr = this->chstr;
      }
   }

   RichString_setChar(this, len, 0);
   this->chlen = len;
}

function: RichString_findChar (!HAVE_LIBNCURSESW)

int RichString_findChar(const RichString* this, char c, int start) {
   const chtype* ch = this->chptr + start;
   for (int i = start; i < this->chlen; i++) {
      if ((*ch & 0xff) == (chtype) c)
         return i;
      ch++;
   }
   return -1;
}

function: RichString_findChar (HAVE_LIBNCURSESW)

int RichString_findChar(const RichString* this, char c, int start) {
   const wchar_t wc = btowc(c);
   const cchar_t* ch = this->chptr + start;
   for (int i = start; i < this->chlen; i++) {
      if (ch->chars[0] == wc)
         return i;
      ch++;
   }
   return -1;
}

function: RichString_rewind

void RichString_rewind(RichString* this, int count) {
   RichString_setLen(this, this->chlen - count);
}

function: RichString_setAttr

void RichString_setAttr(RichString* this, int attrs) {
   RichString_setAttrn(this, attrs, 0, this->chlen);
}

function: RichString_setAttrn (!HAVE_LIBNCURSESW)

void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
   int end = CLAMP(start + charcount, 0, this->chlen);
   for (int i = start; i < end; i++) {
      this->chptr[i] = (this->chptr[i] & 0xff) | attrs;
   }
}

function: RichString_setAttrn (HAVE_LIBNCURSESW)

inline void RichString_setAttrn(RichString* this, int attrs, int start, int charcount) {
   int end = CLAMP(start + charcount, 0, this->chlen);
   for (int i = start; i < end; i++) {
      this->chptr[i].attr = attrs;
   }
}

function: RichString_setLen

static void RichString_setLen(RichString* this, int len) {
   if (len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) {
      RichString_setChar(this, len, 0);
      this->chlen = len;
   } else {
      RichString_extendLen(this, len);
   }
}

function: RichString_writeAscii

int RichString_writeAscii(RichString* this, int attrs, const char* data) {
   return RichString_writeFromAscii(this, attrs, data, 0, strlen(data));
}

function: RichString_writeFromAscii (!HAVE_LIBNCURSESW)

static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data_c, int from, int len) {
   return RichString_writeFromWide(this, attrs, data_c, from, len);
}

function: RichString_writeFromAscii (HAVE_LIBNCURSESW)

static inline int RichString_writeFromAscii(RichString* this, int attrs, const char* data, int from, int len) {
   int newLen = from + len;
   RichString_setLen(this, newLen);
   for (int i = from, j = 0; i < newLen; i++, j++) {
      assert((unsigned char)data[j] <= SCHAR_MAX);
      this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint((unsigned char)data[j]) ? data[j] : L'\xFFFD') } };
   }

   return len;
}

function: RichString_writeFromWide (!HAVE_LIBNCURSESW)

static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
   int newLen = from + len;
   RichString_setLen(this, newLen);
   for (int i = from, j = 0; i < newLen; i++, j++) {
      this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
   }
   this->chptr[newLen] = 0;

   return len;
}

function: RichString_writeFromWide (HAVE_LIBNCURSESW)

static inline int RichString_writeFromWide(RichString* this, int attrs, const char* data_c, int from, int len) {
   wchar_t data[len];
   len = mbstowcs_nonfatal(data, data_c, len);
   if (len <= 0)
      return 0;

   int newLen = from + len;
   RichString_setLen(this, newLen);
   for (int i = from, j = 0; i < newLen; i++, j++) {
      this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : L'\xFFFD') } };
   }

   return len;
}

function: RichString_writeWide

int RichString_writeWide(RichString* this, int attrs, const char* data) {
   return RichString_writeFromWide(this, attrs, data, 0, strlen(data));
}

RichString.h

RichString.h

function: RichString_appendAscii

int RichString_appendAscii(RichString* this, int attrs, const char* data);

function: RichString_appendChr

void RichString_appendChr(RichString* this, int attrs, char c, int count);

function: RichString_appendnAscii

ATTR_ACCESS3_R(3, 4)
int RichString_appendnAscii(RichString* this, int attrs, const char* data, int len);

function: RichString_appendnWide

ATTR_ACCESS3_R(3, 4)
int RichString_appendnWide(RichString* this, int attrs, const char* data, int len);

function: RichString_appendnWideColumns

int RichString_appendnWideColumns(RichString* this, int attrs, const char* data, int len, int* columns);

function: RichString_appendWide

int RichString_appendWide(RichString* this, int attrs, const char* data);

function: RichString_delete

void RichString_delete(RichString* this);

function: RichString_findChar

int RichString_findChar(const RichString* this, char c, int start);

function: RichString_rewind

void RichString_rewind(RichString* this, int count);

function: RichString_setAttr

void RichString_setAttr(RichString* this, int attrs);

function: RichString_setAttrn

void RichString_setAttrn(RichString* this, int attrs, int start, int charcount);

function: RichString_writeAscii

int RichString_writeAscii(RichString* this, int attrs, const char* data);

function: RichString_writeWide

int RichString_writeWide(RichString* this, int attrs, const char* data);

macro: CharType

#ifdef HAVE_LIBNCURSESW
#define CharType cchar_t
#else
#define CharType chtype
#endif

macro: RichString_begin

#define RichString_begin(this) RichString this; RichString_beginAllocated(this)

macro: RichString_beginAllocated

#define RichString_beginAllocated(this)   \
   do {                                   \
      (this).chlen = 0;                   \
      (this).chptr = (this).chstr;        \
      RichString_setChar(&(this), 0, 0);  \
      (this).highlightAttr = 0;           \
   } while(0)

macro: RichString_getCharVal

#ifdef HAVE_LIBNCURSESW
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0])
#else
#define RichString_getCharVal(this, i) ((this).chptr[i] & 0xff)
#endif

macro: RICHSTRING_MAXLEN

#define RICHSTRING_MAXLEN 350

macro: RichString_printoffnVal

#ifdef HAVE_LIBNCURSESW
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + (off), n)
#else
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + (off), n)
#endif

macro: RichString_printVal

#ifdef HAVE_LIBNCURSESW
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
#else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
#endif

macro: RichString_setChar

#ifdef HAVE_LIBNCURSESW
#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0)
#else
#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0)
#endif

macro: RichString_size

#define RichString_size(this) ((this)->chlen)

macro: RichString_sizeVal

#define RichString_sizeVal(this) ((this).chlen)

struct: RichString

typedef struct RichString_ {
   int chlen;
   CharType* chptr;
   CharType chstr[RICHSTRING_MAXLEN + 1];
   int highlightAttr;
} RichString;

Row.c

Row.c

function: alignedTitleDynamicColumn

static const char* alignedTitleDynamicColumn(const Settings* settings, int key, char* titleBuffer, size_t titleBufferSize) {
   const DynamicColumn* column = Hashtable_get(settings->dynamicColumns, key);
   if (column == NULL)
      return "- ";

   int width = column->width;
   if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
      width = DYNAMIC_DEFAULT_COLUMN_WIDTH;

   xSnprintf(titleBuffer, titleBufferSize, "%*s ", width, column->heading);
   return titleBuffer;
}

function: alignedTitleProcessField

static const char* alignedTitleProcessField(ProcessField field, char* titleBuffer, size_t titleBufferSize) {
   const char* title = Process_fields[field].title;
   if (!title)
      return "- ";

   if (Process_fields[field].pidColumn) {
      xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_pidDigits, title);
      return titleBuffer;
   }

   if (field == ST_UID) {
      xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_uidDigits, title);
      return titleBuffer;
   }

   if (Process_fields[field].autoWidth) {
      if (Process_fields[field].autoTitleRightAlign)
         xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_fieldWidths[field], title);
      else
         xSnprintf(titleBuffer, titleBufferSize, "%-*.*s ", Row_fieldWidths[field], Row_fieldWidths[field], title);
      return titleBuffer;
   }

   return title;
}

function: Row_compare

int Row_compare(const void* v1, const void* v2) {
   const Row* r1 = (const Row*)v1;
   const Row* r2 = (const Row*)v2;

   return SPACESHIP_NUMBER(r1->id, r2->id);
}

function: Row_compareByParent_Base

int Row_compareByParent_Base(const void* v1, const void* v2) {
   const Row* r1 = (const Row*)v1;
   const Row* r2 = (const Row*)v2;

   int result = SPACESHIP_NUMBER(
      r1->isRoot ? 0 : Row_getGroupOrParent(r1),
      r2->isRoot ? 0 : Row_getGroupOrParent(r2)
   );

   if (result != 0)
      return result;

   return Row_compare(v1, v2);
}

function: Row_display

void Row_display(const Object* cast, RichString* out) {
   const Row* this = (const Row*) cast;
   const Settings* settings = this->host->settings;
   const RowField* fields = settings->ss->fields;

   for (int i = 0; fields[i]; i++)
      As_Row(this)->writeField(this, out, fields[i]);

   if (Row_isHighlighted(this))
      RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);

   if (this->tag == true)
      RichString_setAttr(out, CRT_colors[PROCESS_TAG]);

   if (settings->highlightChanges) {
      if (Row_isTomb(this))
         out->highlightAttr = CRT_colors[PROCESS_TOMB];
      else if (Row_isNew(this))
         out->highlightAttr = CRT_colors[PROCESS_NEW];
   }

   assert(RichString_size(out) > 0);
}

function: Row_done

void Row_done(Row* this) {
   assert(this != NULL);
   (void) this;
}

function: Row_init

void Row_init(Row* this, const Machine* host) {
   this->host = host;
   this->tag = false;
   this->showChildren = true;
   this->show = true;
   this->wasShown = false;
   this->updated = false;
}

function: Row_isNew

static inline bool Row_isNew(const Row* this) {
   const Machine* host = this->host;
   if (host->monotonicMs < this->seenStampMs)
      return false;

   const Settings* settings = host->settings;
   return host->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)settings->highlightDelaySecs;
}

function: Row_isTomb

static inline bool Row_isTomb(const Row* this) {
   return this->tombStampMs > 0;
}

function: Row_printBytes

void Row_printBytes(RichString* str, unsigned long long number, bool coloring) {
   if (number == ULLONG_MAX)
      Row_printKBytes(str, ULLONG_MAX, coloring);
   else
      Row_printKBytes(str, number / ONE_K, coloring);
}

function: Row_printCount

void Row_printCount(RichString* str, unsigned long long number, bool coloring) {
   char buffer[13];

   int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
   int megabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
   int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];
   int baseColor = CRT_colors[PROCESS];

   if (number == ULLONG_MAX) {
      RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], "        N/A ");
   } else if (number >= 100000LL * ONE_DECIMAL_T) {
      xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
      RichString_appendnAscii(str, largeNumberColor, buffer, 12);
   } else if (number >= 100LL * ONE_DECIMAL_T) {
      xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
      RichString_appendnAscii(str, largeNumberColor, buffer, 8);
      RichString_appendnAscii(str, megabytesColor, buffer + 8, 4);
   } else if (number >= 10LL * ONE_DECIMAL_G) {
      xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
      RichString_appendnAscii(str, largeNumberColor, buffer, 5);
      RichString_appendnAscii(str, megabytesColor, buffer + 5, 3);
      RichString_appendnAscii(str, baseColor, buffer + 8, 4);
   } else {
      xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
      RichString_appendnAscii(str, largeNumberColor, buffer, 2);
      RichString_appendnAscii(str, megabytesColor, buffer + 2, 3);
      RichString_appendnAscii(str, baseColor, buffer + 5, 3);
      RichString_appendnAscii(str, shadowColor, buffer + 8, 4);
   }
}

function: Row_printKBytes

function: Row_printLeftAlignedField

void Row_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) {
   int columns = width;
   RichString_appendnWideColumns(str, attr, content, strlen(content), &columns);
   RichString_appendChr(str, attr, ' ', width + 1 - columns);
}

function: Row_printNanoseconds

void Row_printNanoseconds(RichString* str, unsigned long long totalNanoseconds, bool coloring) {
   if (totalNanoseconds == 0) {
      int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];

      RichString_appendAscii(str, shadowColor, "     0ns ");
      return;
   }

   char buffer[10];
   int len;
   int baseColor = CRT_colors[PROCESS];

   if (totalNanoseconds < 1000000) {
      len = xSnprintf(buffer, sizeof(buffer), "%6luns ", (unsigned long)totalNanoseconds);
      RichString_appendnAscii(str, baseColor, buffer, len);
      return;
   }

   unsigned long long totalMicroseconds = totalNanoseconds / 1000;
   if (totalMicroseconds < 1000000) {
      len = xSnprintf(buffer, sizeof(buffer), ".%06lus ", (unsigned long)totalMicroseconds);
      RichString_appendnAscii(str, baseColor, buffer, len);
      return;
   }

   unsigned long long totalSeconds = totalMicroseconds / 1000000;
   unsigned long microseconds = totalMicroseconds % 1000000;
   if (totalSeconds < 60) {
      int width = 5;
      unsigned long fraction = microseconds / 10;
      if (totalSeconds >= 10) {
         width--;
         fraction /= 10;
      }
      len = xSnprintf(buffer, sizeof(buffer), "%u.%0*lus ", (unsigned int)totalSeconds, width, fraction);
      RichString_appendnAscii(str, baseColor, buffer, len);
      return;
   }

   if (totalSeconds < 600) {
      unsigned int minutes = totalSeconds / 60;
      unsigned int seconds = totalSeconds % 60;
      unsigned int milliseconds = microseconds / 1000;
      len = xSnprintf(buffer, sizeof(buffer), "%u:%02u.%03u ", minutes, seconds, milliseconds);
      RichString_appendnAscii(str, baseColor, buffer, len);
      return;
   }

   unsigned long long totalHundredths = totalMicroseconds / 1000 / 10;
   Row_printTime(str, totalHundredths, coloring);
}

function: Row_printPercentage

int Row_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr) {
   assert(n >= 6 && width >= 4 && "Invalid width in Row_printPercentage()");
   // truncate in favour of abort in xSnprintf()
   width = (uint8_t)CLAMP(width, 4, n - 2);
   assert(width < n - 1 && "Insufficient space to print column");

   if (isNonnegative(val)) {
      if (val < 0.05F)
         *attr = CRT_colors[PROCESS_SHADOW];
      else if (val >= 99.9F)
         *attr = CRT_colors[PROCESS_MEGABYTES];

      int precision = 1;

      // Display "val" as "100" for columns like "MEM%".
      if (width == 4 && val > 99.9F) {
         precision = 0;
         val = 100.0F;
      }

      return xSnprintf(buffer, n, "%*.*f ", width, precision, val);
   }

   *attr = CRT_colors[PROCESS_SHADOW];
   return xSnprintf(buffer, n, "%*.*s ", width, width, "N/A");
}

function: Row_printRate

void Row_printRate(RichString* str, double rate, bool coloring) {
   char buffer[16];

   int largeNumberColor = CRT_colors[LARGE_NUMBER];
   int megabytesColor = CRT_colors[PROCESS_MEGABYTES];
   int shadowColor = CRT_colors[PROCESS_SHADOW];
   int baseColor = CRT_colors[PROCESS];

   if (!coloring) {
      largeNumberColor = CRT_colors[PROCESS];
      megabytesColor = CRT_colors[PROCESS];
   }

   if (!isNonnegative(rate)) {
      RichString_appendAscii(str, shadowColor, "        N/A ");
   } else if (rate < 0.005) {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate);
      RichString_appendnAscii(str, shadowColor, buffer, len);
   } else if (rate < ONE_K) {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate);
      RichString_appendnAscii(str, baseColor, buffer, len);
   } else if (rate < ONE_M) {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f K/s ", rate / ONE_K);
      RichString_appendnAscii(str, baseColor, buffer, len);
   } else if (rate < ONE_G) {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f M/s ", rate / ONE_M);
      RichString_appendnAscii(str, megabytesColor, buffer, len);
   } else if (rate < ONE_T) {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f G/s ", rate / ONE_G);
      RichString_appendnAscii(str, largeNumberColor, buffer, len);
   } else if (rate < ONE_P) {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f T/s ", rate / ONE_T);
      RichString_appendnAscii(str, largeNumberColor, buffer, len);
   } else {
      int len = snprintf(buffer, sizeof(buffer), "%7.2f P/s ", rate / ONE_P);
      RichString_appendnAscii(str, largeNumberColor, buffer, len);
   }
}

function: Row_printTime

function: Row_resetFieldWidths

void Row_resetFieldWidths(void) {
   for (size_t i = 0; i < LAST_PROCESSFIELD; i++) {
      if (!Process_fields[i].autoWidth)
         continue;

      size_t len = strlen(Process_fields[i].title);
      assert(len <= UINT8_MAX);
      Row_fieldWidths[i] = (uint8_t)len;
   }
}

function: Row_setPidColumnWidth

void Row_setPidColumnWidth(pid_t maxPid) {
   if (maxPid < (int)pow(10, ROW_MIN_PID_DIGITS)) {
      Row_pidDigits = ROW_MIN_PID_DIGITS;
      return;
   }

   Row_pidDigits = countDigits((size_t)maxPid, 10);
   assert(Row_pidDigits <= ROW_MAX_PID_DIGITS);
}

function: Row_setUidColumnWidth

void Row_setUidColumnWidth(uid_t maxUid) {
   if (maxUid < (uid_t)pow(10, ROW_MIN_UID_DIGITS)) {
      Row_uidDigits = ROW_MIN_UID_DIGITS;
      return;
   }

   Row_uidDigits = countDigits((size_t)maxUid, 10);
   assert(Row_uidDigits <= ROW_MAX_UID_DIGITS);
}

function: Row_toggleTag

void Row_toggleTag(Row* this) {
   this->tag = !this->tag;
}

function: Row_updateFieldWidth

void Row_updateFieldWidth(RowField key, size_t width) {
   if (width > UINT8_MAX)
      Row_fieldWidths[key] = UINT8_MAX;
   else if (width > Row_fieldWidths[key])
      Row_fieldWidths[key] = (uint8_t)width;
}

function: RowField_alignedTitle

const char* RowField_alignedTitle(const Settings* settings, RowField field) {
   static char titleBuffer[UINT8_MAX + sizeof(" ")];
   assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" "));
   assert(sizeof(titleBuffer) >= ROW_MAX_PID_DIGITS + sizeof(" "));
   assert(sizeof(titleBuffer) >= ROW_MAX_UID_DIGITS + sizeof(" "));

   if (field < LAST_PROCESSFIELD)
      return alignedTitleProcessField((ProcessField)field, titleBuffer, sizeof(titleBuffer));
   return alignedTitleDynamicColumn(settings, field, titleBuffer, sizeof(titleBuffer));
}

function: RowField_keyAt

RowField RowField_keyAt(const Settings* settings, int at) {
   const RowField* fields = (const RowField*) settings->ss->fields;
   RowField field;
   int x = 0;
   for (int i = 0; (field = fields[i]); i++) {
      int len = strlen(RowField_alignedTitle(settings, field));
      if (at >= x && at <= x + len) {
         return field;
      }
      x += len;
   }
   return COMM;
}

global_variable: Row_fieldWidths

uint8_t Row_fieldWidths[LAST_PROCESSFIELD] = { 0 };

global_variable: Row_pidDigits

int Row_pidDigits = ROW_MIN_PID_DIGITS;

global_variable: Row_uidDigits

int Row_uidDigits = ROW_MIN_UID_DIGITS;

struct: Row_class

const RowClass Row_class = {
   .super = {
      .extends = Class(Object),
      .compare = Row_compare
   },
};

Row.h

Row.h

function prototype: Row_compare

int Row_compare(const void* v1, const void* v2);

function prototype: Row_compareByParent_Base

int Row_compareByParent_Base(const void* v1, const void* v2);

function prototype: Row_display

void Row_display(const Object* cast, RichString* out);

function prototype: Row_done

void Row_done(Row* this);

function prototype: Row_init

void Row_init(Row* this, const struct Machine_* host);

function prototype: Row_printBytes

void Row_printBytes(RichString* str, unsigned long long number, bool coloring);

function prototype: Row_printCount

void Row_printCount(RichString* str, unsigned long long number, bool coloring);

function prototype: Row_printKBytes

void Row_printKBytes(RichString* str, unsigned long long number, bool coloring);

function prototype: Row_printLeftAlignedField

void Row_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);

function prototype: Row_printNanoseconds

void Row_printNanoseconds(RichString* str, unsigned long long totalNanoseconds, bool coloring);

function prototype: Row_printPercentage

int Row_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr);

function prototype: Row_printRate

void Row_printRate(RichString* str, double rate, bool coloring);

function prototype: Row_printTime

void Row_printTime(RichString* str, unsigned long long totalHundredths, bool coloring);

function prototype: Row_resetFieldWidths

void Row_resetFieldWidths(void);

function prototype: Row_setPidColumnWidth

void Row_setPidColumnWidth(pid_t maxPid);

function prototype: Row_setUidColumnWidth

void Row_setUidColumnWidth(uid_t maxUid);

function prototype: Row_toggleTag

void Row_toggleTag(Row* this);

function prototype: Row_updateFieldWidth

void Row_updateFieldWidth(RowField key, size_t width);

function prototype: RowField_alignedTitle

const char* RowField_alignedTitle(const struct Settings_* settings, RowField field);

function prototype: RowField_keyAt

RowField RowField_keyAt(const struct Settings_* settings, int at);

global variable: Row_class

extern const RowClass Row_class;

global variable: Row_fieldWidths

extern uint8_t Row_fieldWidths[LAST_RESERVED_FIELD];

global variable: Row_pidDigits

extern int Row_pidDigits;

global variable: Row_uidDigits

extern int Row_uidDigits;

inline function: Row_getGroupOrParent

static inline int Row_getGroupOrParent(const Row* this) {
   return this->group == this->id ? this->parent : this->group;
}

inline function: Row_idEqualCompare

static inline int Row_idEqualCompare(const void* v1, const void* v2) {
   const int p1 = ((const Row*)v1)->id;
   const int p2 = ((const Row*)v2)->id;
   return p1 != p2; /* return zero when equal */
}

inline function: Row_isChildOf

static inline bool Row_isChildOf(const Row* this, int id) {
   return id == Row_getGroupOrParent(this);
}

macro: As_Row

#define As_Row(this_)  ((const RowClass*)((this_)->super.klass))

macro: ONE_DECIMAL_G

#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)

macro: ONE_DECIMAL_K

#define ONE_DECIMAL_K 1000UL

macro: ONE_DECIMAL_M

#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)

macro: ONE_DECIMAL_P

#define ONE_DECIMAL_P (1ULL * ONE_DECIMAL_T * ONE_DECIMAL_K)

macro: ONE_DECIMAL_T

#define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)

macro: ONE_G

#define ONE_G (ONE_M * ONE_K)

macro: ONE_K

#define ONE_K 1024UL

macro: ONE_M

#define ONE_M (ONE_K * ONE_K)

macro: ONE_P

#define ONE_P (1ULL * ONE_T * ONE_K)

macro: ONE_T

#define ONE_T (1ULL * ONE_G * ONE_K)

macro: Row_compareByParent

#define Row_compareByParent(r1_, r2_)  (As_Row(r1_)->compareByParent ? (As_Row(r1_)->compareByParent(r1_, r2_)) : Row_compareByParent_Base(r1_, r2_))

macro: Row_isHighlighted

#define Row_isHighlighted(r_)  (As_Row(r_)->isHighlighted ? (As_Row(r_)->isHighlighted(r_)) : false)

macro: Row_isVisible

#define Row_isVisible(r_, t_)  (As_Row(r_)->isVisible ? (As_Row(r_)->isVisible(r_, t_)) : true)

macro: Row_matchesFilter

#define Row_matchesFilter(r_, t_)  (As_Row(r_)->matchesFilter ? (As_Row(r_)->matchesFilter(r_, t_)) : false)

macro: ROW_MAX_PID_DIGITS

#define ROW_MAX_PID_DIGITS 19

macro: ROW_MAX_UID_DIGITS

#define ROW_MAX_UID_DIGITS 20

macro: ROW_MIN_PID_DIGITS

#define ROW_MIN_PID_DIGITS 5

macro: ROW_MIN_UID_DIGITS

#define ROW_MIN_UID_DIGITS 5

macro: Row_sortKeyString

#define Row_sortKeyString(r_)  (As_Row(r_)->sortKeyString ? (As_Row(r_)->sortKeyString(r_)) : "")

struct: Row_

typedef struct Row_ {
   /* Super object for emulated OOP */
   Object super;

   /* Pointer to quasi-global data */
   const struct Machine_* host;

   int id;
   int group;
   int parent;

   /* Has no known parent */
   bool isRoot;

   /* Whether the row was tagged by the user */
   bool tag;

   /* Whether to display this row */
   bool show;

   /* Whether this row was shown last cycle */
   bool wasShown;

   /* Whether to show children of this row in tree-mode */
   bool showChildren;

   /* Whether the row was updated during the last scan */
   bool updated;

   /*
    * Internal state for tree-mode.
    */
   int32_t indent;
   unsigned int tree_depth;

   /*
    * Internal time counts for showing new and exited processes.
    */
   uint64_t seenStampMs;
   uint64_t tombStampMs;
} Row;

struct: RowClass_

typedef struct RowClass_ {
   const ObjectClass super;
   const Row_IsHighlighted isHighlighted;
   const Row_IsVisible isVisible;
   const Row_WriteField writeField;
   const Row_MatchesFilter matchesFilter;
   const Row_SortKeyString sortKeyString;
   const Row_CompareByParent compareByParent;
} RowClass;

struct forward declaration: Machine_

struct Machine_;

struct forward declaration: Settings_

struct Settings_;

struct forward declaration: Table_

struct Table_;

typedef: Row

typedef struct Row_ {
   /* Super object for emulated OOP */
   Object super;

   /* Pointer to quasi-global data */
   const struct Machine_* host;

   int id;
   int group;
   int parent;

   /* Has no known parent */
   bool isRoot;

   /* Whether the row was tagged by the user */
   bool tag;

   /* Whether to display this row */
   bool show;

   /* Whether this row was shown last cycle */
   bool wasShown;

   /* Whether to show children of this row in tree-mode */
   bool showChildren;

   /* Whether the row was updated during the last scan */
   bool updated;

   /*
    * Internal state for tree-mode.
    */
   int32_t indent;
   unsigned int tree_depth;

   /*
    * Internal time counts for showing new and exited processes.
    */
   uint64_t seenStampMs;
   uint64_t tombStampMs;
} Row;

typedef: RowClass

typedef struct RowClass_ {
   const ObjectClass super;
   const Row_IsHighlighted isHighlighted;
   const Row_IsVisible isVisible;
   const Row_WriteField writeField;
   const Row_MatchesFilter matchesFilter;
   const Row_SortKeyString sortKeyString;
   const Row_CompareByParent compareByParent;
} RowClass;

typedef function pointer: Row_CompareByParent

typedef int (*Row_CompareByParent)(const Row*, const Row*);

typedef function pointer: Row_IsHighlighted

typedef bool (*Row_IsHighlighted)(const Row*);

typedef function pointer: Row_IsVisible

typedef bool (*Row_IsVisible)(const Row*, const struct Table_*);

typedef function pointer: Row_MatchesFilter

typedef bool (*Row_MatchesFilter)(const Row*, const struct Table_*);

typedef function pointer: Row_New

typedef Row* (*Row_New)(const struct Machine_*);

typedef function pointer: Row_SortKeyString

typedef const char* (*Row_SortKeyString)(Row*);

typedef function pointer: Row_WriteField

typedef void (*Row_WriteField)(const Row*, RichString*, RowField);

RowField.h

RowField.h

macro: ROW_DYNAMIC_FIELDS

#define ROW_DYNAMIC_FIELDS LAST_RESERVED_FIELD

typedef: RowField

typedef int32_t RowField;

typedef enum: ReservedFields

typedef enum ReservedFields_ {
   NULL_FIELD = 0,
   PID = 1,
   COMM = 2,
   STATE = 3,
   PPID = 4,
   PGRP = 5,
   SESSION = 6,
   TTY = 7,
   TPGID = 8,
   MINFLT = 10,
   MAJFLT = 12,
   PRIORITY = 18,
   NICE = 19,
   STARTTIME = 21,
   PROCESSOR = 38,
   M_VIRT = 39,
   M_RESIDENT = 40,
   ST_UID = 46,
   PERCENT_CPU = 47,
   PERCENT_MEM = 48,
   USER = 49,
   TIME = 50,
   NLWP = 51,
   TGID = 52,
   PERCENT_NORM_CPU = 53,
   ELAPSED = 54,
   SCHEDULERPOLICY = 55,
   PROC_COMM = 124,
   PROC_EXE = 125,
   CWD = 126,

   /* Platform specific fields, defined in ${platform}/ProcessField.h */
   PLATFORM_PROCESS_FIELDS,

   /* Do not add new fields after this entry (dynamic entries follow) */
   LAST_RESERVED_FIELD
} ReservedFields;

Scheduling.c

Scheduling.c

array: policies

static const SchedulingPolicy policies[] = {
   [SCHED_OTHER] = { "Other",      SCHED_OTHER, false },
#ifdef SCHED_BATCH
   [SCHED_BATCH] = { "Batch",      SCHED_BATCH, false },
#endif
#ifdef SCHED_IDLE
   [SCHED_IDLE]  = { "Idle",       SCHED_IDLE,  false },
#endif
   [SCHED_FIFO]  = { "FiFo",       SCHED_FIFO,  true },
   [SCHED_RR]    = { "RoundRobin", SCHED_RR,    true },
};

function: Scheduling_formatPolicy

const char* Scheduling_formatPolicy(int policy) {
#ifdef SCHED_RESET_ON_FORK
   policy = policy & ~SCHED_RESET_ON_FORK;
#endif

   switch (policy) {
      case SCHED_OTHER:
         return "OTHER";
      case SCHED_FIFO:
         return "FIFO";
      case SCHED_RR:
         return "RR";
#ifdef SCHED_BATCH
      case SCHED_BATCH:
         return "BATCH";
#endif
#ifdef SCHED_IDLE
      case SCHED_IDLE:
         return "IDLE";
#endif
#ifdef SCHED_DEADLINE
      case SCHED_DEADLINE:
         return "EDF";
#endif
      default:
         return "???";
   }
}

function: Scheduling_newPolicyPanel

Panel* Scheduling_newPolicyPanel(int preSelectedPolicy) {
   Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel "));
   Panel_setHeader(this, "New policy:");

#ifdef SCHED_RESET_ON_FORK
   Panel_add(this, (Object*) ListItem_new(reset_on_fork ? "Reset on fork: on" : "Reset on fork: off", -1));
#endif

   for (unsigned i = 0; i < ARRAYSIZE(policies); i++) {
      if (!policies[i].name)
         continue;

      Panel_add(this, (Object*) ListItem_new(policies[i].name, policies[i].id));
      if (policies[i].id == preSelectedPolicy)
         Panel_setSelected(this, i);
   }

   return this;
}

function: Scheduling_newPriorityPanel

Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority) {
   if (policy < 0 || (unsigned)policy >= ARRAYSIZE(policies) || policies[policy].name == NULL)
      return NULL;

   if (!policies[policy].prioritySupport)
      return NULL;

   int min = sched_get_priority_min(policy);
   if (min < 0)
      return NULL;
   int max = sched_get_priority_max(policy);
   if (max < 0 )
      return NULL;

   Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel "));
   Panel_setHeader(this, "Priority:");

   for (int i = min; i <= max; i++) {
      char buf[16];
      xSnprintf(buf, sizeof(buf), "%d", i);
      Panel_add(this, (Object*) ListItem_new(buf, i));
      if (i == preSelectedPriority)
         Panel_setSelected(this, i);
   }

   return this;
}

function: Scheduling_readProcessPolicy

void Scheduling_readProcessPolicy(Process* proc) {
   proc->scheduling_policy = sched_getscheduler(Process_getPid(proc));
}

function: Scheduling_rowSetPolicy

bool Scheduling_rowSetPolicy(Row* row, Arg arg) {
   Process* p = (Process*) row;
   assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class));
   return Scheduling_setPolicy(p, arg);
}

function: Scheduling_setPolicy

static bool Scheduling_setPolicy(Process* p, Arg arg) {
   const SchedulingArg* sarg = arg.v;
   int policy = sarg->policy;

   assert(policy >= 0);
   assert((unsigned)policy < ARRAYSIZE(policies));
   assert(policies[policy].name);

   const struct sched_param param = { .sched_priority = policies[policy].prioritySupport ? sarg->priority : 0 };

   #ifdef SCHED_RESET_ON_FORK
   if (reset_on_fork)
      policy &= SCHED_RESET_ON_FORK;
   #endif

   int r = sched_setscheduler(Process_getPid(p), policy, &param);

   /* POSIX says on success the previous scheduling policy should be returned,
    * but Linux always returns 0. */
   return r != -1;
}

function: Scheduling_togglePolicyPanelResetOnFork

void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel) {
#ifdef SCHED_RESET_ON_FORK
   reset_on_fork = !reset_on_fork;

   ListItem* item = (ListItem*) Panel_get(schedPanel, 0);

   free_and_xStrdup(&item->value, reset_on_fork ? "Reset on fork: on" : "Reset on fork: off");
#else
   (void)schedPanel;
#endif
}

variable: reset_on_fork

#ifdef SCHED_RESET_ON_FORK
static bool reset_on_fork = false;
#endif

Scheduling.h

Scheduling.h

function: Scheduling_formatPolicy

const char* Scheduling_formatPolicy(int policy);

function: Scheduling_newPolicyPanel

Panel* Scheduling_newPolicyPanel(int preSelectedPolicy);

function: Scheduling_newPriorityPanel

Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority);

function: Scheduling_readProcessPolicy

void Scheduling_readProcessPolicy(Process* proc);

function: Scheduling_rowSetPolicy

bool Scheduling_rowSetPolicy(Row* proc, Arg arg);

function: Scheduling_togglePolicyPanelResetOnFork

void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel);

macro: SCHEDULER_SUPPORT

#define SCHEDULER_SUPPORT

macro: SCHEDULINGPANEL_INITSELECTEDPOLICY

#define SCHEDULINGPANEL_INITSELECTEDPOLICY SCHED_OTHER

macro: SCHEDULINGPANEL_INITSELECTEDPRIORITY

#define SCHEDULINGPANEL_INITSELECTEDPRIORITY 50

struct: SchedulingArg

typedef struct {
   int policy;
   int priority;
} SchedulingArg;

struct: SchedulingPolicy

typedef struct {
   const char* name;
   int id;
   bool prioritySupport;
} SchedulingPolicy;

ScreenManager.c

ScreenManager.c

function: checkRecalculation

static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw) {
   Machine* host = this->host;

   Platform_gettime_realtime(&host->realtime, &host->realtimeMs);
   double newTime = ((double)host->realtime.tv_sec * 10) + ((double)host->realtime.tv_usec / 100000);

   *timedOut = (newTime - *oldTime > host->settings->delay);
   *rescan |= *timedOut;

   if (newTime < *oldTime) {
      *rescan = true; // clock was adjusted?
   }

   if (*rescan) {
      *oldTime = newTime;
      int oldUidDigits = Process_uidDigits;
      if (!this->state->pauseUpdate && (*sortTimeout == 0 || host->settings->ss->treeView)) {
         host->activeTable->needsSort = true;
         *sortTimeout = 1;
      }
      // sample current values for system metrics and processes if not paused
      Machine_scan(host);
      if (!this->state->pauseUpdate)
         Machine_scanTables(host);

      // always update header, especially to avoid gaps in graph meters
      Header_updateData(this->header);
      // force redraw if the number of UID digits was changed
      if (Process_uidDigits != oldUidDigits) {
         *force_redraw = true;
      }
      *redraw = true;
   }
   if (*redraw) {
      Table_rebuildPanel(host->activeTable);
      if (!this->state->hideMeters)
         Header_draw(this->header);
   }
   *rescan = false;
}

function: drawTab

static inline bool drawTab(const int* y, int* x, int l, const char* name, bool cur) {
   attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
   mvaddch(*y, *x, '[');
   (*x)++;
   if (*x >= l)
      return false;
   int nameLen = strlen(name);
   int n = MINIMUM(l - *x, nameLen);
   attrset(CRT_colors[cur ? SCREENS_CUR_TEXT : SCREENS_OTH_TEXT]);
   mvaddnstr(*y, *x, name, n);
   *x += n;
   if (*x >= l)
      return false;
   attrset(CRT_colors[cur ? SCREENS_CUR_BORDER : SCREENS_OTH_BORDER]);
   mvaddch(*y, *x, ']');
   *x += 1 + SCREEN_TAB_COLUMN_GAP;
   if (*x >= l)
      return false;
   return true;
}

function: header_height

static int header_height(const ScreenManager* this) {
   if (this->state->hideMeters)
      return 0;

   if (this->header)
      return this->header->height;

   return 0;
}

function: ScreenManager_add

void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
   ScreenManager_insert(this, item, size, Vector_size(this->panels));
}

function: ScreenManager_delete

void ScreenManager_delete(ScreenManager* this) {
   Vector_delete(this->panels);
   free(this);
}

function: ScreenManager_drawPanels

static void ScreenManager_drawPanels(ScreenManager* this, int focus, bool force_redraw) {
   Settings* settings = this->host->settings;
   if (settings->screenTabs) {
      ScreenManager_drawScreenTabs(this);
   }
   const int nPanels = this->panelCount;
   for (int i = 0; i < nPanels; i++) {
      Panel* panel = (Panel*) Vector_get(this->panels, i);
      Panel_draw(panel,
                 force_redraw,
                 i == focus,
                 panel != (Panel*)this->state->mainPanel || !this->state->hideSelection,
                 State_hideFunctionBar(this->state));
      mvvline(panel->y, panel->x + panel->w, ' ', panel->h + (State_hideFunctionBar(this->state) ? 1 : 0));
   }
}

function: ScreenManager_drawScreenTabs

static void ScreenManager_drawScreenTabs(ScreenManager* this) {
   Settings* settings = this->host->settings;
   ScreenSettings** screens = settings->screens;
   int cur = settings->ssIndex;
   int l = COLS;
   Panel* panel = (Panel*) Vector_get(this->panels, 0);
   int y = panel->y - 1;
   int x = SCREEN_TAB_MARGIN_LEFT;

   if (this->name) {
      drawTab(&y, &x, l, this->name, true);
      return;
   }

   for (int s = 0; screens[s]; s++) {
      bool ok = drawTab(&y, &x, l, screens[s]->heading, s == cur);
      if (!ok) {
         break;
      }
   }
   attrset(CRT_colors[RESET_COLOR]);
}

function: ScreenManager_insert

void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
   int lastX = 0;
   if (idx > 0) {
      const Panel* last = (const Panel*) Vector_get(this->panels, idx - 1);
      lastX = last->x + last->w + 1;
   }
   int height = LINES - this->y1 - header_height(this) + this->y2;
   if (size <= 0) {
      size = COLS - this->x1 + this->x2 - lastX;
   }
   Panel_resize(item, size, height);
   Panel_move(item, lastX, this->y1 + header_height(this));
   if (idx < this->panelCount) {
      for (int i =  idx + 1; i <= this->panelCount; i++) {
         Panel* p = (Panel*) Vector_get(this->panels, i);
         Panel_move(p, p->x + size, p->y);
      }
   }
   Vector_insert(this->panels, idx, item);
   item->needsRedraw = true;
   this->panelCount++;
}

function: ScreenManager_new

ScreenManager* ScreenManager_new(Header* header, Machine* host, State* state, bool owner) {
   ScreenManager* this;
   this = xMalloc(sizeof(ScreenManager));
   this->x1 = 0;
   this->y1 = 0;
   this->x2 = 0;
   this->y2 = -1;
   this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
   this->panelCount = 0;
   this->header = header;
   this->host = host;
   this->state = state;
   this->allowFocusChange = true;
   return this;
}

function: ScreenManager_remove

Panel* ScreenManager_remove(ScreenManager* this, int idx) {
   assert(this->panelCount > idx);
   int w = ((Panel*) Vector_get(this->panels, idx))->w;
   Panel* panel = (Panel*) Vector_remove(this->panels, idx);
   this->panelCount--;
   if (idx < this->panelCount) {
      for (int i = idx; i < this->panelCount; i++) {
         Panel* p = (Panel*) Vector_get(this->panels, i);
         Panel_move(p, p->x - w, p->y);
      }
   }
   return panel;
}

function: ScreenManager_resize

void ScreenManager_resize(ScreenManager* this) {
   int y1_header = this->y1 + header_height(this);
   int panels = this->panelCount;
   int lastX = 0;
   for (int i = 0; i < panels - 1; i++) {
      Panel* panel = (Panel*) Vector_get(this->panels, i);
      Panel_resize(panel, panel->w, LINES - y1_header + this->y2);
      Panel_move(panel, lastX, y1_header);
      lastX = panel->x + panel->w + 1;
   }
   Panel* panel = (Panel*) Vector_get(this->panels, panels - 1);
   Panel_resize(panel, COLS - this->x1 + this->x2 - lastX, LINES - y1_header + this->y2);
   Panel_move(panel, lastX, y1_header);
}

function: ScreenManager_run

function: ScreenManager_size

inline int ScreenManager_size(const ScreenManager* this) {
   return this->panelCount;
}

ScreenManager.h

ScreenManager.h

method: ScreenManager_add

void ScreenManager_add(ScreenManager* this, Panel* item, int size);

method: ScreenManager_delete

void ScreenManager_delete(ScreenManager* this);

method: ScreenManager_insert

void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);

method: ScreenManager_new

ScreenManager* ScreenManager_new(Header* header, Machine* host, State* state, bool owner);

method: ScreenManager_remove

Panel* ScreenManager_remove(ScreenManager* this, int idx);

method: ScreenManager_resize

void ScreenManager_resize(ScreenManager* this);

method: ScreenManager_run

void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey, const char* name);

method: ScreenManager_size

int ScreenManager_size(const ScreenManager* this);

struct: ScreenManager

typedef struct ScreenManager_ {
   int x1;
   int y1;
   int x2;
   int y2;
   Vector* panels;
   const char* name;
   int panelCount;
   Header* header;
   Machine* host;
   State* state;
   bool allowFocusChange;
} ScreenManager;

ScreensPanel.c

ScreensPanel.c

function: addNewScreen

static void addNewScreen(Panel* super) {
   ScreensPanel* const this = (ScreensPanel*) super;

   const char* name = "New";
   ScreenSettings* ss = Settings_newScreen(this->settings, &(const ScreenDefaults) { .name = name, .columns = "PID Command", .sortKey = "PID" });
   ScreenListItem* item = ScreenListItem_new(name, ss);
   int idx = Panel_getSelectedIndex(super);
   Panel_insert(super, idx + 1, (Object*) item);
   Panel_setSelected(super, idx + 1);
}

function: rebuildSettingsArray

static void rebuildSettingsArray(Panel* super, int selected) {
   ScreensPanel* const this = (ScreensPanel*) super;

   int n = Panel_size(super);
   free(this->settings->screens);
   this->settings->screens = xMallocArray(n + 1, sizeof(ScreenSettings*));
   this->settings->screens[n] = NULL;
   for (int i = 0; i < n; i++) {
      ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
      this->settings->screens[i] = item->ss;
   }
   this->settings->nScreens = n;
   /* ensure selection is in valid range */
   if (selected > n - 1)
      selected = n - 1;
   else if (selected < 0)
      selected = 0;
   this->settings->ssIndex = selected;
   this->settings->ss = this->settings->screens[selected];
}

function: ScreenListItem_delete

static void ScreenListItem_delete(Object* cast) {
   ScreenListItem* this = (ScreenListItem*)cast;
   if (this->ss) {
      ScreenSettings_delete(this->ss);
   }
   ListItem_delete(cast);
}

function: ScreenListItem_new

ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
   ScreenListItem* this = AllocThis(ScreenListItem);
   ListItem_init((ListItem*)this, value, 0);
   this->ss = ss;
   return this;
}

function: ScreensPanel_delete

static void ScreensPanel_delete(Object* object) {
   Panel* super = (Panel*) object;

   /* do not delete screen settings still in use */
   int n = Panel_size(super);
   for (int i = 0; i < n; i++) {
      ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
      item->ss = NULL;
   }

   Panel_delete(object);
}

function: ScreensPanel_eventHandler

static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
   ScreensPanel* const this = (ScreensPanel*) super;

   if (this->renamingItem) {
      return ScreensPanel_eventHandlerRenaming(super, ch);
   } else {
      return ScreensPanel_eventHandlerNormal(super, ch);
   }
}

function: ScreensPanel_eventHandlerNormal

function: ScreensPanel_eventHandlerRenaming

static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
   ScreensPanel* const this = (ScreensPanel*) super;

   if (ch >= 32 && ch < 127 && ch != '=') {
      if (this->cursor < SCREEN_NAME_LEN - 1) {
         this->buffer[this->cursor] = (char)ch;
         this->cursor++;
         super->selectedLen = strlen(this->buffer);
         Panel_setCursorToSelection(super);
      }

      return HANDLED;
   }

   switch (ch) {
      case 127:
      case KEY_BACKSPACE:
         if (this->cursor > 0) {
            this->cursor--;
            this->buffer[this->cursor] = '\0';
            super->selectedLen = strlen(this->buffer);
            Panel_setCursorToSelection(super);
         }
         break;
      case '\n':
      case '\r':
      case KEY_ENTER: {
         ListItem* item = (ListItem*) Panel_getSelected(super);
         if (!item)
            break;
         assert(item == this->renamingItem);
         free(this->saved);
         item->value = xStrdup(this->buffer);
         this->renamingItem = NULL;
         super->cursorOn = false;
         Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
         ScreensPanel_update(super);
         break;
      }
      case 27: { // Esc
         ListItem* item = (ListItem*) Panel_getSelected(super);
         if (!item)
            break;
         assert(item == this->renamingItem);
         item->value = this->saved;
         this->renamingItem = NULL;
         super->cursorOn = false;
         Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
         break;
      }
   }

   return HANDLED;
}

function: ScreensPanel_new

ScreensPanel* ScreensPanel_new(Settings* settings) {
   ScreensPanel* this = AllocThis(ScreensPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(settings->dynamicScreens ? DynamicFunctions : ScreensFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   Hashtable* columns = settings->dynamicColumns;

   this->settings = settings;
   this->columns = ColumnsPanel_new(settings->screens[0], columns, &(settings->changed));
   this->availableColumns = AvailableColumnsPanel_new((Panel*) this->columns, columns);
   this->moving = false;
   this->renamingItem = NULL;
   super->cursorOn = false;
   this->cursor = 0;
   Panel_setHeader(super, "Screens");

   for (unsigned int i = 0; i < settings->nScreens; i++) {
      ScreenSettings* ss = settings->screens[i];
      char* name = ss->heading;
      Panel_add(super, (Object*) ScreenListItem_new(name, ss));
   }
   return this;
}

function: ScreensPanel_update

void ScreensPanel_update(Panel* super) {
   ScreensPanel* this = (ScreensPanel*) super;
   int size = Panel_size(super);
   this->settings->changed = true;
   this->settings->lastUpdate++;
   this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*));
   for (int i = 0; i < size; i++) {
      ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
      ScreenSettings* ss = item->ss;
      free_and_xStrdup(&ss->heading, ((ListItem*) item)->value);
      this->settings->screens[i] = ss;
   }
   this->settings->screens[size] = NULL;
}

function: startRenaming

static void startRenaming(Panel* super) {
   ScreensPanel* const this = (ScreensPanel*) super;

   ListItem* item = (ListItem*) Panel_getSelected(super);
   if (item == NULL)
      return;
   this->renamingItem = item;
   super->cursorOn = true;
   char* name = item->value;
   this->saved = name;
   strncpy(this->buffer, name, SCREEN_NAME_LEN);
   this->buffer[SCREEN_NAME_LEN] = '\0';
   this->cursor = strlen(this->buffer);
   item->value = this->buffer;
   Panel_setSelectionColor(super, PANEL_EDIT);
   super->selectedLen = strlen(this->buffer);
   Panel_setCursorToSelection(super);
}

global_variable: DynamicFunctions

static const char* const DynamicFunctions[] = {"      ", "Rename", "      ", "      ", "      ", "      ", "MoveUp", "MoveDn", "Remove", "Done  ", NULL};

global_variable: ScreensFunctions

static const char* const ScreensFunctions[] = {"      ", "Rename", "      ", "      ", "New   ", "      ", "MoveUp", "MoveDn", "Remove", "Done  ", NULL};

struct: ScreenListItem_class

ObjectClass ScreenListItem_class = {
   .extends = Class(ListItem),
   .display = ListItem_display,
   .delete = ScreenListItem_delete,
   .compare = ListItem_compare
};

struct: ScreensPanel_class

PanelClass ScreensPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = ScreensPanel_delete
   },
   .eventHandler = ScreensPanel_eventHandler
};

ScreensPanel.h

ScreensPanel.h

function prototype: ScreenListItem_new

ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss);

function prototype: ScreensPanel_new

ScreensPanel* ScreensPanel_new(Settings* settings);

function prototype: ScreensPanel_update

void ScreensPanel_update(Panel* super);

global variable declaration: ScreenListItem_class

extern ObjectClass ScreenListItem_class;

struct definition: ScreenListItem

typedef struct ScreenListItem_ {
   ListItem super;
   DynamicScreen* ds;
   ScreenSettings* ss;
} ScreenListItem;

struct definition: ScreensPanel

typedef struct ScreensPanel_ {
   Panel super;

   ScreenManager* scr;
   Settings* settings;
   ColumnsPanel* columns;
   AvailableColumnsPanel* availableColumns;
   char buffer[SCREEN_NAME_LEN + 1];
   char* saved;
   int cursor;
   bool moving;
   ListItem* renamingItem;
} ScreensPanel;

ScreenTabsPanel.c

ScreenTabsPanel.c

function: addDynamicScreen

static void addDynamicScreen(ATTR_UNUSED ht_key_t key, void* value, void* userdata) {
   DynamicScreen* screen = (DynamicScreen*) value;
   Panel* super = (Panel*) userdata;
   const char* name = screen->heading ? screen->heading : screen->name;

   Panel_add(super, (Object*) ScreenTabListItem_new(name, screen));
}

function: addNewScreen

static void addNewScreen(Panel* super, DynamicScreen* ds) {
   ScreenNamesPanel* const this = (ScreenNamesPanel*) super;
   const char* name = "New";
   ScreenSettings* ss = (ds != NULL) ? Settings_newDynamicScreen(this->settings, name, ds, NULL) : Settings_newScreen(this->settings, &(const ScreenDefaults) { .name = name, .columns = "PID Command", .sortKey = "PID" });
   ScreenNameListItem* item = ScreenNameListItem_new(name, ss);
   int idx = Panel_getSelectedIndex(super);
   Panel_insert(super, idx + 1, (Object*) item);
   Panel_setSelected(super, idx + 1);
}

function: renameScreenSettings

static void renameScreenSettings(ScreenNamesPanel* this, const ListItem* item) {
   const ScreenNameListItem* nameItem = (const ScreenNameListItem*) item;

   ScreenSettings* ss = nameItem->ss;
   free_and_xStrdup(&ss->heading, item->value);

   Settings* settings = this->settings;
   settings->changed = true;
   settings->lastUpdate++;
}

function: ScreenNameListItem_new

ScreenNameListItem* ScreenNameListItem_new(const char* value, ScreenSettings* ss) {
   ScreenNameListItem* this = AllocThis(ScreenNameListItem);
   ListItem_init((ListItem*)this, value, 0);
   this->ss = ss;
   return this;
}

function: ScreenNamesPanel_delete

static void ScreenNamesPanel_delete(Object* object) {
   ScreenNamesPanel* this = (ScreenNamesPanel*) object;
   Panel* super = &this->super;

   /* do not delete screen settings still in use */
   int n = Panel_size(super);
   for (int i = 0; i < n; i++) {
      ScreenNameListItem* item = (ScreenNameListItem*) Panel_get(super, i);
      item->ss = NULL;
   }

   /* during renaming the ListItem's value points to our static buffer */
   if (this->renamingItem)
      this->renamingItem->value = this->saved;

   Panel_done(super);
   free(this);
}

function: ScreenNamesPanel_eventHandler

static HandlerResult ScreenNamesPanel_eventHandler(Panel* super, int ch) {
   ScreenNamesPanel* const this = (ScreenNamesPanel*) super;

   if (!this->renamingItem)
      return ScreenNamesPanel_eventHandlerNormal(super, ch);
   return ScreenNamesPanel_eventHandlerRenaming(super, ch);
}

function: ScreenNamesPanel_eventHandlerNormal

static HandlerResult ScreenNamesPanel_eventHandlerNormal(Panel* super, int ch) {
   ScreenNamesPanel* const this = (ScreenNamesPanel*) super;
   ScreenNameListItem* oldFocus = (ScreenNameListItem*) Panel_getSelected(super);
   HandlerResult result = IGNORED;

   switch (ch) {
      case '\n':
      case '\r':
      case KEY_ENTER:
      case KEY_MOUSE:
      case KEY_RECLICK:
         Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
         result = HANDLED;
         break;
      case EVENT_SET_SELECTED:
         result = HANDLED;
         break;
      case KEY_NPAGE:
      case KEY_PPAGE:
      case KEY_HOME:
      case KEY_END:
         Panel_onKey(super, ch);
         break;
      case KEY_F(5):
      case KEY_CTRL('N'):
         addNewScreen(super, this->ds);
         startRenaming(super);
         result = HANDLED;
         break;
      default:
         if (ch < 255 && isalpha(ch))
            result = Panel_selectByTyping(super, ch);
         if (result == BREAK_LOOP)
            result = IGNORED;
         break;
   }

   ScreenNameListItem* newFocus = (ScreenNameListItem*) Panel_getSelected(super);
   if (newFocus && oldFocus != newFocus)
      result = HANDLED;

   return result;
}

function: ScreenNamesPanel_eventHandlerRenaming

static HandlerResult ScreenNamesPanel_eventHandlerRenaming(Panel* super, int ch) {
   ScreenNamesPanel* const this = (ScreenNamesPanel*) super;

   if (ch >= 32 && ch < 127 && ch != '=') {
      if (this->cursor < SCREEN_NAME_LEN - 1) {
         this->buffer[this->cursor] = (char)ch;
         this->cursor++;
         super->selectedLen = strlen(this->buffer);
         Panel_setCursorToSelection(super);
      }

      return HANDLED;
   }

   switch (ch) {
      case 127:
      case KEY_BACKSPACE:
         if (this->cursor > 0) {
            this->cursor--;
            this->buffer[this->cursor] = '\0';
            super->selectedLen = strlen(this->buffer);
            Panel_setCursorToSelection(super);
         }
         break;
      case '\n':
      case '\r':
      case KEY_ENTER: {
         ListItem* item = (ListItem*) Panel_getSelected(super);
         if (!item)
            break;
         assert(item == this->renamingItem);
         free(this->saved);
         item->value = xStrdup(this->buffer);
         this->renamingItem = NULL;
         super->cursorOn = false;
         Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
         renameScreenSettings(this, item);
         break;
      }
      case 27: { // Esc
         ListItem* item = (ListItem*) Panel_getSelected(super);
         if (!item)
            break;
         assert(item == this->renamingItem);
         item->value = this->saved;
         this->renamingItem = NULL;
         super->cursorOn = false;
         Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS);
         break;
      }
   }

   return HANDLED;
}

function: ScreenNamesPanel_fill

static void ScreenNamesPanel_fill(ScreenNamesPanel* this, DynamicScreen* ds) {
   const Settings* settings = this->settings;
   Panel* super = &this->super;
   Panel_prune(super);

   for (unsigned int i = 0; i < settings->nScreens; i++) {
      const ScreenSettings* ss = settings->screens[i];

      if (ds == NULL) {
         if (ss->dynamic != NULL)
            continue;
         /* built-in (processes, not dynamic) - e.g. Main or I/O */
      } else {
         if (ss->dynamic == NULL)
            continue;
         if (!String_eq(ds->name, ss->dynamic))
            continue;
         /* matching dynamic screen found, add it into the Panel */
      }
      Panel_add(super, (Object*) ListItem_new(ss->heading, i));
   }

   this->ds = ds;
}

function: ScreenNamesPanel_new

ScreenNamesPanel* ScreenNamesPanel_new(Settings* settings) {
   ScreenNamesPanel* this = AllocThis(ScreenNamesPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(ScreenNamesFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   this->settings = settings;
   this->renamingItem = NULL;
   memset(this->buffer, 0, sizeof(this->buffer));
   this->ds = NULL;
   this->saved = NULL;
   this->cursor = 0;
   super->cursorOn = false;
   Panel_setHeader(super, "Screens");

   for (unsigned int i = 0; i < settings->nScreens; i++) {
      ScreenSettings* ss = settings->screens[i];
      /* initially show only for Processes tabs (selected) */
      if (ss->dynamic)
         continue;
      Panel_add(super, (Object*) ScreenNameListItem_new(ss->heading, ss));
   }
   return this;
}

function: ScreenTabListItem_new

static ScreenTabListItem* ScreenTabListItem_new(const char* value, DynamicScreen* ds) {
   ScreenTabListItem* this = AllocThis(ScreenTabListItem);
   ListItem_init((ListItem*)this, value, 0);
   this->ds = ds;
   return this;
}

function: ScreenTabsPanel_delete

static void ScreenTabsPanel_delete(Object* object) {
   ScreenTabsPanel* this = (ScreenTabsPanel*) object;
   Panel_done(&this->super);
   free(this);
}

function: ScreenTabsPanel_eventHandler

static HandlerResult ScreenTabsPanel_eventHandler(Panel* super, int ch) {
   ScreenTabsPanel* const this = (ScreenTabsPanel* const) super;

   HandlerResult result = IGNORED;

   int selected = Panel_getSelectedIndex(super);
   switch (ch) {
      case EVENT_SET_SELECTED:
         result = HANDLED;
         break;
      case KEY_F(5):
      case KEY_CTRL('N'):
         /* pass onto the Names panel for creating new screen */
         return ScreenNamesPanel_eventHandlerNormal(&this->names->super, ch);
      case KEY_UP:
      case KEY_DOWN:
      case KEY_NPAGE:
      case KEY_PPAGE:
      case KEY_HOME:
      case KEY_END: {
         int previous = selected;
         Panel_onKey(super, ch);
         selected = Panel_getSelectedIndex(super);
         if (previous != selected)
            result = HANDLED;
         break;
      }
      default:
         if (ch < 255 && isalpha(ch))
            result = Panel_selectByTyping(super, ch);
         if (result == BREAK_LOOP)
            result = IGNORED;
         break;
   }

   if (result == HANDLED) {
      ScreenTabListItem* focus = (ScreenTabListItem*) Panel_getSelected(super);
      if (focus) {
         ScreenNamesPanel_fill(this->names, focus->ds);
      }
   }

   return result;
}

function: ScreenTabsPanel_new

ScreenTabsPanel* ScreenTabsPanel_new(Settings* settings) {
   ScreenTabsPanel* this = AllocThis(ScreenTabsPanel);
   Panel* super = &this->super;

   FunctionBar* fuBar = FunctionBar_new(ScreenTabsFunctions, NULL, NULL);
   Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);

   this->settings = settings;
   this->names = ScreenNamesPanel_new(settings);
   super->cursorOn = false;
   this->cursor = 0;
   Panel_setHeader(super, "Screen tabs");

   assert(settings->dynamicScreens != NULL);
   Panel_add(super, (Object*) ScreenTabListItem_new("Processes", NULL));
   Hashtable_foreach(settings->dynamicScreens, addDynamicScreen, super);

   return this;
}

function: startRenaming

static void startRenaming(Panel* super) {
   ScreenNamesPanel* const this = (ScreenNamesPanel*) super;

   ListItem* item = (ListItem*) Panel_getSelected(super);
   if (item == NULL)
      return;

   this->renamingItem = item;
   super->cursorOn = true;
   char* name = item->value;
   this->saved = name;
   strncpy(this->buffer, name, SCREEN_NAME_LEN);
   this->buffer[SCREEN_NAME_LEN] = '\0';
   this->cursor = strlen(this->buffer);
   item->value = this->buffer;
   Panel_setSelectionColor(super, PANEL_EDIT);
   super->selectedLen = strlen(this->buffer);
   Panel_setCursorToSelection(super);
}

global variable: ScreenNameListItem_class

ObjectClass ScreenNameListItem_class = {
   .extends = Class(ListItem),
   .display = ListItem_display,
   .delete = ListItem_delete,
   .compare = ListItem_compare
};

global variable: ScreenNamesFunctions

static const char* const ScreenNamesFunctions[] = {"      ", "      ", "      ", "      ", "New   ", "      ", "      ", "      ", "      ", "Done  ", NULL};

global variable: ScreenNamesPanel_class

PanelClass ScreenNamesPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = ScreenNamesPanel_delete
   },
   .eventHandler = ScreenNamesPanel_eventHandler
};

global variable: ScreenTabListItem_class

ObjectClass ScreenTabListItem_class = {
   .extends = Class(ListItem),
   .display = ListItem_display,
   .delete = ListItem_delete,
   .compare = ListItem_compare
};

global variable: ScreenTabsFunctions

static const char* const ScreenTabsFunctions[] = {"      ", "      ", "      ", "      ", "New   ", "      ", "      ", "      ", "      ", "Done  ", NULL};

global variable: ScreenTabsPanel_class

PanelClass ScreenTabsPanel_class = {
   .super = {
      .extends = Class(Panel),
      .delete = ScreenTabsPanel_delete,
   },
   .eventHandler = ScreenTabsPanel_eventHandler
};

ScreenTabsPanel.h

ScreenTabsPanel.h

external variable: ScreenNameListItem_class

extern ObjectClass ScreenNameListItem_class;

external variable: ScreenNamesPanel_class

extern PanelClass ScreenNamesPanel_class;

function: ScreenNameListItem_new

ScreenNameListItem* ScreenNameListItem_new(const char* value, ScreenSettings* ss);

function: ScreenNamesPanel_new

ScreenNamesPanel* ScreenNamesPanel_new(Settings* settings);

function: ScreenTabsPanel_new

ScreenTabsPanel* ScreenTabsPanel_new(Settings* settings);

struct: ScreenNameListItem_

typedef struct ScreenNameListItem_ {
   ListItem super;
   ScreenSettings* ss;
} ScreenNameListItem;

struct: ScreenNamesPanel_

typedef struct ScreenNamesPanel_ {
   Panel super;

   ScreenManager* scr;
   Settings* settings;
   char buffer[SCREEN_NAME_LEN + 1];
   DynamicScreen* ds;
   char* saved;
   int cursor;
   ListItem* renamingItem;
} ScreenNamesPanel;

struct: ScreenTabListItem_

typedef struct ScreenTabListItem_ {
   ListItem super;
   DynamicScreen* ds;
} ScreenTabListItem;

struct: ScreenTabsPanel_

typedef struct ScreenTabsPanel_ {
   Panel super;

   ScreenManager* scr;
   Settings* settings;
   ScreenNamesPanel* names;
   int cursor;
} ScreenTabsPanel;

htop_suppressions.valgrind

scripts/htop_suppressions.valgrind

file: htop_suppressions.valgrind

{
   <ncurses internal memory>
   Memcheck:Leak
   match-leak-kinds: possible,reachable
   ...
   fun:doupdate_sp
   fun:wrefresh
}

{
   <ncurses internal memory>
   Memcheck:Leak
   match-leak-kinds: possible,reachable
   ...
   fun:newterm_sp
   fun:newterm
   fun:initscr
   fun:CRT_init
}

{
   <ncurses internal memory>
   Memcheck:Leak
   match-leak-kinds: reachable
   ...
   obj:*/libtinfo*
   fun:CRT_init
}

{
   <ncurses internal memory>
   Memcheck:Leak
   match-leak-kinds: reachable
   ...
   obj:*/libncurses*
   fun:CRT_init
}

{
   <ncurses internal memory>
   Memcheck:Leak
   match-leak-kinds: possible,reachable
   ...
   obj:*/libncurses*
   fun:CRT_setColors
   fun:CRT_init
}

{
   <devstat internal memory>
   Memcheck:Leak
   match-leak-kinds: possible,reachable
   ...
   obj:*/libdevstat*
   ...
   fun:Platform_getDiskIO
}

run_valgrind.sh

scripts/run_valgrind.sh

function: main

#!/bin/sh

SCRIPT=$(readlink -f "$0")
SCRIPTDIR=$(dirname "$SCRIPT")

valgrind --leak-check=full --show-reachable=yes --show-leak-kinds=all --track-fds=yes --errors-for-leak-kinds=all --track-origins=yes --suppressions="${SCRIPTDIR}/htop_suppressions.valgrind" "${SCRIPTDIR}/../htop"

Settings.c

Settings.c

function: ScreenSettings_delete

void ScreenSettings_delete(ScreenSettings* this) {
   free(this->heading);
   free(this->dynamic);
   free(this->fields);
   free(this);
}

function: ScreenSettings_invertSortOrder

void ScreenSettings_invertSortOrder(ScreenSettings* this) {
   int* attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
   *attr = (*attr == 1) ? -1 : 1;
}

function: ScreenSettings_readFields

static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, const char* line) {
   char* trim = String_trim(line);
   char** ids = String_split(trim, ' ', NULL);
   free(trim);

   /* reset default fields */
   memset(ss->fields, '\0', LAST_PROCESSFIELD * sizeof(ProcessField));

   for (size_t j = 0, i = 0; ids[i]; i++) {
      if (j >= UINT_MAX / sizeof(ProcessField))
         continue;
      if (j >= LAST_PROCESSFIELD) {
         ss->fields = xRealloc(ss->fields, (j + 1) * sizeof(ProcessField));
         memset(&ss->fields[j], 0, sizeof(ProcessField));
      }
      int id = toFieldIndex(columns, ids[i]);
      if (id >= 0)
         ss->fields[j++] = id;
      if (id > 0 && id < LAST_PROCESSFIELD)
         ss->flags |= Process_fields[id].flags;
   }
   String_freeArray(ids);
}

function: ScreenSettings_setSortKey

void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey) {
   if (this->treeViewAlwaysByPID || !this->treeView) {
      this->sortKey = sortKey;
      this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
      this->treeView = false;
   } else {
      this->treeSortKey = sortKey;
      this->treeDirection = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
   }
}

function: Settings_defaultMeters

function: Settings_defaultScreens

static ScreenSettings* Settings_defaultScreens(Settings* this) {
   if (this->nScreens)
      return this->screens[0];
   for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
      const ScreenDefaults* defaults = &Platform_defaultScreens[i];
      Settings_newScreen(this, defaults);
   }
   Platform_defaultDynamicScreens(this);
   return this->screens[0];
}

function: Settings_delete

void Settings_delete(Settings* this) {
   free(this->filename);
   free(this->initialFilename);
   Settings_deleteColumns(this);
   Settings_deleteScreens(this);
   free(this);
}

function: Settings_deleteColumns

static void Settings_deleteColumns(Settings* this) {
   for (size_t i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
      String_freeArray(this->hColumns[i].names);
      free(this->hColumns[i].modes);
   }
   free(this->hColumns);
}

function: Settings_deleteScreens

static void Settings_deleteScreens(Settings* this) {
   if (this->screens) {
      for (size_t i = 0; this->screens[i]; i++)
         ScreenSettings_delete(this->screens[i]);
      free(this->screens);
   }
}

function: Settings_enableReadonly

static bool readonly = false;

void Settings_enableReadonly(void) {
   readonly = true;
}

function: Settings_initScreenSettings

static ScreenSettings* Settings_initScreenSettings(ScreenSettings* ss, Settings* this, const char* columns) {
   ScreenSettings_readFields(ss, this->dynamicColumns, columns);
   this->screens[this->nScreens] = ss;
   this->nScreens++;
   this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
   this->screens[this->nScreens] = NULL;
   return ss;
}

function: Settings_isReadonly

bool Settings_isReadonly(void) {
   return readonly;
}

function: Settings_new

function: Settings_newDynamicScreen

ScreenSettings* Settings_newDynamicScreen(Settings* this, const char* tab, const DynamicScreen* screen, Table* table) {
   int sortKey = toFieldIndex(this->dynamicColumns, screen->columnKeys);

   ScreenSettings* ss = xMalloc(sizeof(ScreenSettings));
   *ss = (ScreenSettings) {
      .heading = xStrdup(tab),
      .dynamic = xStrdup(screen->name),
      .table = table,
      .fields = xCalloc(LAST_PROCESSFIELD, sizeof(ProcessField)),
      .direction = screen->direction,
      .treeDirection = 1,
      .sortKey = sortKey,
   };
   return Settings_initScreenSettings(ss, this, screen->columnKeys);
}

function: Settings_newScreen

ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* defaults) {
   int sortKey = defaults->sortKey ? toFieldIndex(this->dynamicColumns, defaults->sortKey) : PID;
   int treeSortKey = defaults->treeSortKey ? toFieldIndex(this->dynamicColumns, defaults->treeSortKey) : PID;
   int sortDesc = (sortKey >= 0 && sortKey < LAST_PROCESSFIELD) ? Process_fields[sortKey].defaultSortDesc : 1;

   ScreenSettings* ss = xMalloc(sizeof(ScreenSettings));
   *ss = (ScreenSettings) {
      .heading = xStrdup(defaults->name),
      .dynamic = NULL,
      .table = NULL,
      .fields = xCalloc(LAST_PROCESSFIELD, sizeof(ProcessField)),
      .flags = 0,
      .direction = sortDesc ? -1 : 1,
      .treeDirection = 1,
      .sortKey = sortKey,
      .treeSortKey = treeSortKey,
      .treeView = false,
      .treeViewAlwaysByPID = false,
      .allBranchesCollapsed = false,
   };
   return Settings_initScreenSettings(ss, this, defaults->columns);
}

function: Settings_read

function: Settings_readMeterModes

static void Settings_readMeterModes(Settings* this, const char* line, size_t column) {
   char** ids = Settings_splitLineToIDs(line);

   size_t len = 0;
   for (size_t i = 0; ids[i]; i++) {
      len++;
   }

   column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
   this->hColumns[column].len = len;
   MeterModeId* modes = len ? xCalloc(len, sizeof(MeterModeId)) : NULL;
   for (size_t i = 0; i < len; i++) {
      modes[i] = (MeterModeId) atoi(ids[i]);
   }
   this->hColumns[column].modes = modes;

   String_freeArray(ids);
}

function: Settings_readMeters

static void Settings_readMeters(Settings* this, const char* line, size_t column) {
   column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
   this->hColumns[column].names = Settings_splitLineToIDs(line);
}

function: Settings_setHeaderLayout

void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) {
   unsigned int oldColumns = HeaderLayout_getColumns(this->hLayout);
   unsigned int newColumns = HeaderLayout_getColumns(hLayout);

   if (newColumns > oldColumns) {
      this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
      memset(this->hColumns + oldColumns, 0, (newColumns - oldColumns) * sizeof(MeterColumnSetting));
   } else if (newColumns < oldColumns) {
      for (unsigned int i = newColumns; i < oldColumns; i++) {
         if (this->hColumns[i].names) {
            for (size_t j = 0; j < this->hColumns[i].len; j++)
               free(this->hColumns[i].names[j]);
            free(this->hColumns[i].names);
         }
         free(this->hColumns[i].modes);
      }
      this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
   }

   this->hLayout = hLayout;
   this->changed = true;
}

function: Settings_splitLineToIDs

static char** Settings_splitLineToIDs(const char* line) {
   char* trim = String_trim(line);
   char** ids = String_split(trim, ' ', NULL);
   free(trim);
   return ids;
}

function: Settings_validateMeters

static bool Settings_validateMeters(Settings* this) {
   const size_t colCount = HeaderLayout_getColumns(this->hLayout);

   bool anyMeter = false;

   for (size_t column = 0; column < colCount; column++) {
      char** names = this->hColumns[column].names;
      const MeterModeId* modes = this->hColumns[column].modes;
      const size_t len = this->hColumns[column].len;

      if (!len)
         continue;

      if (!names || !modes)
         return false;

      anyMeter |= !!len;

      // Check for each mode there is an entry with a non-NULL name
      for (size_t meterIdx = 0; meterIdx < len; meterIdx++)
         if (!names[meterIdx])
            return false;

      if (names[len])
         return false;
   }

   return anyMeter;
}

function: Settings_write

function: signal_safe_fprintf

ATTR_FORMAT(printf, 2, 3)
static int signal_safe_fprintf(FILE* stream, const char* fmt, ...) {
   char buf[2048];

   va_list vl;
   va_start(vl, fmt);
   int n = vsnprintf(buf, sizeof(buf), fmt, vl);
   va_end(vl);

   if (n <= 0)
      return n;

   return full_write_str(fileno(stream), buf);
}

function: toFieldIndex

static int toFieldIndex(Hashtable* columns, const char* str) {
   if (isdigit((unsigned char)str[0])) {
      // This "+1" is for compatibility with the older enum format.
      int id = atoi(str) + 1;
      if (toFieldName(columns, id, NULL)) {
         return id;
      }
   } else {
      // Dynamically-defined columns are always stored by-name.
      char dynamic[32] = {0};
      if (sscanf(str, "Dynamic(%30s)", dynamic) == 1) {
         char* end;
         if ((end = strrchr(dynamic, ')')) != NULL) {
            bool success;
            unsigned int key;
            *end = '\0';
            success = DynamicColumn_search(columns, dynamic, &key) != NULL;
            *end = ')';
            if (success)
               return key;
         }
      }
      // Fallback to iterative scan of table of fields by-name.
      for (int p = 1; p < LAST_PROCESSFIELD; p++) {
         const char* pName = toFieldName(columns, p, NULL);
         if (pName && strcmp(pName, str) == 0)
            return p;
      }
   }
   return -1;
}

function: toFieldName

static const char* toFieldName(Hashtable* columns, int id, bool* enabled) {
   if (id < 0) {
      if (enabled)
         *enabled = false;
      return NULL;
   }
   if (id >= ROW_DYNAMIC_FIELDS) {
      const DynamicColumn* column = DynamicColumn_lookup(columns, id);
      if (enabled)
         *enabled = column ? column->enabled : false;
      return column ? column->name : NULL;
   }
   if (enabled)
      *enabled = true;
   return Process_fields[id].name;
}

function: writeFields

static void writeFields(OutputFunc of, FILE* fp,
                        const ProcessField* fields, Hashtable* columns,
                        bool byName, char separator) {
   const char* sep = "";
   for (unsigned int i = 0; fields[i]; i++) {
      if (fields[i] < LAST_PROCESSFIELD && byName) {
         const char* pName = toFieldName(columns, fields[i], NULL);
         of(fp, "%s%s", sep, pName);
      } else if (fields[i] >= LAST_PROCESSFIELD && byName) {
         bool enabled;
         const char* pName = toFieldName(columns, fields[i], &enabled);
         if (enabled)
            of(fp, "%sDynamic(%s)", sep, pName);
      } else {
         // This "-1" is for compatibility with the older enum format.
         of(fp, "%s%d", sep, (int) fields[i] - 1);
      }
      sep = " ";
   }
   of(fp, "%c", separator);
}

function: writeList

static void writeList(OutputFunc of, FILE* fp,
                      char** list, int len, char separator) {
   const char* sep = "";
   for (int i = 0; i < len; i++) {
      of(fp, "%s%s", sep, list[i]);
      sep = " ";
   }
   of(fp, "%c", separator);
}

function: writeMeterModes

static void writeMeterModes(const Settings* this, OutputFunc of,
                            FILE* fp, char separator, unsigned int column) {
   if (this->hColumns[column].len) {
      const char* sep = "";
      for (size_t i = 0; i < this->hColumns[column].len; i++) {
         of(fp, "%s%u", sep, this->hColumns[column].modes[i]);
         sep = " ";
      }
   } else {
      of(fp, "!");
   }

   of(fp, "%c", separator);
}

function: writeMeters

static void writeMeters(const Settings* this, OutputFunc of,
                        FILE* fp, char separator, unsigned int column) {
   if (this->hColumns[column].len) {
      writeList(of, fp, this->hColumns[column].names, this->hColumns[column].len, separator);
   } else {
      of(fp, "!%c", separator);
   }
}

struct: ScreenSettings

/* Struct definition is not in this file, but implied by usage */

struct: Settings

/* Struct definition is not in this file, but implied by usage */

Settings.h

Settings.h

function: ScreenSettings_delete

void ScreenSettings_delete(ScreenSettings* this);

function: ScreenSettings_getActiveDirection

static inline int ScreenSettings_getActiveDirection(const ScreenSettings* this) {
   return this->treeView ? this->treeDirection : this->direction;
}

function: ScreenSettings_getActiveSortKey

static inline RowField ScreenSettings_getActiveSortKey(const ScreenSettings* this) {
   return (this->treeView)
          ? (this->treeViewAlwaysByPID ? 1 : this->treeSortKey)
          : this->sortKey;
}

function: ScreenSettings_invertSortOrder

void ScreenSettings_invertSortOrder(ScreenSettings* this);

function: ScreenSettings_setSortKey

void ScreenSettings_setSortKey(ScreenSettings* this, RowField sortKey);

function: Settings_delete

void Settings_delete(Settings* this);

function: Settings_enableReadonly

void Settings_enableReadonly(void);

function: Settings_isReadonly

bool Settings_isReadonly(void);

function: Settings_new

Settings* Settings_new(const struct Machine_* host, Hashtable* dynamicMeters, Hashtable* dynamicColumns, Hashtable* dynamicScreens);

function: Settings_newDynamicScreen

ScreenSettings* Settings_newDynamicScreen(Settings* this, const char* tab, const struct DynamicScreen_* screen, struct Table_* table);

function: Settings_newScreen

ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* defaults);

function: Settings_setHeaderLayout

void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout);

function: Settings_write

int Settings_write(const Settings* this, bool onCrash);

macro: CONFIG_READER_MIN_VERSION

#define CONFIG_READER_MIN_VERSION 3

macro: DEFAULT_DELAY

#define DEFAULT_DELAY 15

macro: Settings_cpuId

#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))

struct: MeterColumnSetting

typedef struct {
   size_t len;
   char** names;
   MeterModeId* modes;
} MeterColumnSetting;

struct: ScreenDefaults

typedef struct {
   const char* name;
   const char* columns;
   const char* sortKey;
   const char* treeSortKey;
} ScreenDefaults;

struct: ScreenSettings

typedef struct ScreenSettings_ {
   char* heading;  /* user-editable screen name (pretty) */
   char* dynamic;  /* from DynamicScreen config (fixed) */
   struct Table_* table;
   RowField* fields;
   uint32_t flags;
   int direction;
   int treeDirection;
   RowField sortKey;
   RowField treeSortKey;
   bool treeView;
   bool treeViewAlwaysByPID;
   bool allBranchesCollapsed;
} ScreenSettings;

struct: Settings

typedef struct Settings_ {
   char* filename;
   char* initialFilename;
   bool writeConfig; /* whether to write the current settings on exit */
   int config_version;
   HeaderLayout hLayout;
   MeterColumnSetting* hColumns;
   Hashtable* dynamicColumns; /* runtime-discovered columns */
   Hashtable* dynamicMeters;  /* runtime-discovered meters */
   Hashtable* dynamicScreens; /* runtime-discovered screens */

   ScreenSettings** screens;
   unsigned int nScreens;
   unsigned int ssIndex;
   ScreenSettings* ss;

   int colorScheme;
   int delay;

   bool countCPUsFromOne;
   bool detailedCPUTime;
   bool showCPUUsage;
   bool showCPUFrequency;
   #ifdef BUILD_WITH_CPU_TEMP
   bool showCPUTemperature;
   bool degreeFahrenheit;
   #endif
   bool showProgramPath;
   bool shadowOtherUsers;
   bool showThreadNames;
   bool hideKernelThreads;
   bool hideRunningInContainer;
   bool hideUserlandThreads;
   bool highlightBaseName;
   bool highlightDeletedExe;
   bool shadowDistPathPrefix;
   bool highlightMegabytes;
   bool highlightThreads;
   bool highlightChanges;
   int highlightDelaySecs;
   bool findCommInCmdline;
   bool stripExeFromCmdline;
   bool showMergedCommand;
   bool updateProcessNames;
   bool accountGuestInCPUMeter;
   bool headerMargin;
   bool screenTabs;
   bool showCachedMemory;
   #ifdef HAVE_GETMOUSE
   bool enableMouse;
   #endif
   int hideFunctionBar;  // 0 - off, 1 - on ESC until next input, 2 - permanently
   #ifdef HAVE_LIBHWLOC
   bool topologyAffinity;
   #endif

   bool changed;
   uint64_t lastUpdate;
} Settings;

SignalsPanel.c

SignalsPanel.c

function: SignalsPanel_new

Panel* SignalsPanel_new(int preSelectedSignal) {
   Panel* this = Panel_new(1, 1, 1, 1, Class(ListItem), true, FunctionBar_newEnterEsc("Send   ", "Cancel "));
   int defaultPosition = 15;
   unsigned int i;
   for (i = 0; i < Platform_numberOfSignals; i++) {
      Panel_set(this, i, (Object*) ListItem_new(Platform_signals[i].name, Platform_signals[i].number));
      // signal 15 is not always the 15th signal in the table
      if (Platform_signals[i].number == preSelectedSignal) {
         defaultPosition = i;
      }
   }
   #if (defined(SIGRTMIN) && defined(SIGRTMAX))
   if (SIGRTMAX - SIGRTMIN <= 100) {
      static char buf[16];
      for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
         int n = sig - SIGRTMIN;
         xSnprintf(buf, sizeof(buf), "%2d SIGRTMIN%-+3d", sig, n);
         if (n == 0) {
            buf[11] = '\0';
         }
         Panel_set(this, i, (Object*) ListItem_new(buf, sig));
      }
   }
   #endif
   Panel_setHeader(this, "Send signal:");
   Panel_setSelected(this, defaultPosition);
   return this;
}

SignalsPanel.h

SignalsPanel.h

function: SignalsPanel_new

Panel* SignalsPanel_new(int preSelectedSignal);

macro: SIGNALSPANEL_INITSELECTEDSIGNAL

#define SIGNALSPANEL_INITSELECTEDSIGNAL SIGTERM

struct: SignalItem

typedef struct SignalItem_ {
   const char* name;
   int number;
} SignalItem;

Platform.c

solaris/Platform.c

function: Platform_buildenv

static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr_t addr, const char* str) {
   envAccum* accump = accum;
   (void) Phandle;
   (void) addr;

   size_t thissz = strlen(str);

   while ((thissz + 2) > (accump->capacity - accump->size)) {
      if (accump->capacity > (SIZE_MAX / 2))
         return 1;

      accump->capacity *= 2;
      accump->env = xRealloc(accump->env, accump->capacity);
   }

   strlcpy( accump->env + accump->size, str, accump->capacity - accump->size);
   strncpy( accump->env + accump->size + thissz + 1, "\n", 2);

   accump->size += thissz + 1;
   return 0;
}

function: Platform_done

void Platform_done(void) {
   /* no platform-specific cleanup needed */
}

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC) {
   *percent = NAN;
   *isOnAC = AC_ERROR;
}

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data) {
   // TODO
   (void)data;
   return false;
}

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max) {
   *used = NAN;
   *max = NAN;
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
   double plat_loadavg[3];
   if (getloadavg( plat_loadavg, 3 ) < 0) {
      *one = NAN;
      *five = NAN;
      *fifteen = NAN;
      return;
   }
   *one = plat_loadavg[LOADAVG_1MIN];
   *five = plat_loadavg[LOADAVG_5MIN];
   *fifteen = plat_loadavg[LOADAVG_15MIN];
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void) {
   int vproc = 32778; // Reasonable Solaris default

   kstat_ctl_t* kc = kstat_open();
   if (kc != NULL) {
      kstat_t* kshandle = kstat_lookup_wrapper(kc, "unix", 0, "var");
      if (kshandle != NULL) {
         kstat_read(kc, kshandle, NULL);

         kvar_t* ksvar = kshandle->ks_data;
         if (ksvar && ksvar->v_proc > 0) {
            vproc = ksvar->v_proc;
         }
      }
      kstat_close(kc);
   }

   return vproc;
}

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data) {
   // TODO
   (void)data;
   return false;
}

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid) {
   envAccum envBuilder;
   pid_t realpid = pid / 1024;
   int graberr;
   struct ps_prochandle* Phandle;

   if ((Phandle = Pgrab(realpid, PGRAB_RDONLY, &graberr)) == NULL) {
      return NULL;
   }

   envBuilder.capacity = 4096;
   envBuilder.size     = 0;
   envBuilder.env      = xMalloc(envBuilder.capacity);

   (void) Penv_iter(Phandle, Platform_buildenv, &envBuilder);

   Prelease(Phandle, 0);

   strncpy( envBuilder.env + envBuilder.size, "\0", 1);

   return xRealloc(envBuilder.env, envBuilder.size + 1);
}

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
   (void)pid;
   return NULL;
}

function: Platform_getUptime

int Platform_getUptime(void) {
   int boot_time = 0;
   int curr_time = time(NULL);
   struct utmpx* ent;

   while (( ent = getutxent() )) {
      if ( String_eq("system boot", ent->ut_line )) {
         boot_time = ent->ut_tv.tv_sec;
      }
   }

   endutxent();

   return (curr_time - boot_time);
}

function: Platform_init

bool Platform_init(void) {
   /* no platform-specific setup needed */
   return true;
}

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys) {
   /* no platform-specific key bindings */
   (void) keys;
}

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu) {
   const Machine* host = this->host;
   const SolarisMachine* shost = (const SolarisMachine*) host;
   unsigned int cpus = host->existingCPUs;
   const CPUData* cpuData = NULL;

   if (cpus == 1) {
      // single CPU box has everything in spl->cpus[0]
      cpuData = &(shost->cpus[0]);
   } else {
      cpuData = &(shost->cpus[cpu]);
   }

   if (!cpuData->online) {
      this->curItems = 0;
      return NAN;
   }

   double percent;
   double* v = this->values;

   v[CPU_METER_NICE]   = cpuData->nicePercent;
   v[CPU_METER_NORMAL] = cpuData->userPercent;
   if (host->settings->detailedCPUTime) {
      v[CPU_METER_KERNEL]  = cpuData->systemPercent;
      v[CPU_METER_IRQ]     = cpuData->irqPercent;
      this->curItems = 4;
   } else {
      v[CPU_METER_KERNEL] = cpuData->systemAllPercent;
      this->curItems = 3;
   }

   percent = sumPositiveValues(v, this->curItems);
   percent = MINIMUM(percent, 100.0);

   v[CPU_METER_FREQUENCY] = cpuData->frequency;
   v[CPU_METER_TEMPERATURE] = NAN;

   return percent;
}

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this) {
   const Machine* host = this->host;
   this->total = host->totalMem;
   this->values[MEMORY_METER_USED] = host->usedMem;
   // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm"
   // this->values[MEMORY_METER_COMPRESSED] = "compressed memory, like zswap on linux"
   this->values[MEMORY_METER_BUFFERS] = host->buffersMem;
   this->values[MEMORY_METER_CACHE] = host->cachedMem;
   // this->values[MEMORY_METER_AVAILABLE] = "available memory"
}

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this) {
   const Machine* host = this->host;
   this->total = host->totalSwap;
   this->values[SWAP_METER_USED] = host->usedSwap;
   // this->values[SWAP_METER_CACHE] = "pages that are both in swap and RAM, like SwapCached on linux"
   // this->values[SWAP_METER_FRONTSWAP] = "pages that are accounted to swap but stored elsewhere, like frontswap on linux"
}

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this) {
   const SolarisMachine* shost = (const SolarisMachine*) this->host;

   ZfsArcMeter_readStats(this, &shost->zfs);
}

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this) {
   const SolarisMachine* shost = (const SolarisMachine*) this->host;

   ZfsCompressedArcMeter_readStats(this, &shost->zfs);
}

variable: Platform_defaultScreens

const ScreenDefaults Platform_defaultScreens[] = {
   {
      .name = "Default",
      .columns = "PID LWPID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
      .sortKey = "PERCENT_CPU",
   },
};

variable: Platform_meterTypes

const MeterClass* const Platform_meterTypes[] = {
   &CPUMeter_class,
   &ClockMeter_class,
   &DateMeter_class,
   &DateTimeMeter_class,
   &LoadAverageMeter_class,
   &LoadMeter_class,
   &MemoryMeter_class,
   &SwapMeter_class,
   &MemorySwapMeter_class,
   &TasksMeter_class,
   &BatteryMeter_class,
   &HostnameMeter_class,
   &SysArchMeter_class,
   &UptimeMeter_class,
   &AllCPUsMeter_class,
   &AllCPUs2Meter_class,
   &AllCPUs4Meter_class,
   &AllCPUs8Meter_class,
   &LeftCPUsMeter_class,
   &RightCPUsMeter_class,
   &LeftCPUs2Meter_class,
   &RightCPUs2Meter_class,
   &LeftCPUs4Meter_class,
   &RightCPUs4Meter_class,
   &LeftCPUs8Meter_class,
   &RightCPUs8Meter_class,
   &ZfsArcMeter_class,
   &ZfsCompressedArcMeter_class,
   &BlankMeter_class,
   NULL
};

variable: Platform_numberOfDefaultScreens

const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);

variable: Platform_numberOfSignals

const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);

variable: Platform_signals

Platform.h

solaris/Platform.h

function: kstat_data_lookup_wrapper

static inline void* kstat_data_lookup_wrapper(kstat_t* ksp, const char* name) {
IGNORE_WCASTQUAL_BEGIN
   return kstat_data_lookup(ksp, (char*)name);
IGNORE_WCASTQUAL_END
}

function: kstat_lookup_wrapper

static inline kstat_t* kstat_lookup_wrapper(kstat_ctl_t* kc, const char* ks_module, int ks_instance, const char* ks_name) {
IGNORE_WCASTQUAL_BEGIN
   return kstat_lookup(kc, (char*)ks_module, ks_instance, (char*)ks_name);
IGNORE_WCASTQUAL_END
}

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

static inline void Platform_getHostname(char* buffer, size_t size) {
   Generic_hostname(buffer, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

static inline void Platform_getRelease(char** string) {
   *string = Generic_uname();
}

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

function: Platform_setZfsArcValues

void Platform_setZfsArcValues(Meter* this);

function: Platform_setZfsCompressedArcValues

void Platform_setZfsCompressedArcValues(Meter* this);

global variable: Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

global variable: Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

global variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

global variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

global variable: Platform_signals

extern const SignalItem Platform_signals[];

macro: ERR

#undef ERR
#include <libproc.h>
#undef ERR
#define ERR (-1)

macro: IGNORE_WCASTQUAL_BEGIN

IGNORE_WCASTQUAL_BEGIN

macro: IGNORE_WCASTQUAL_END

IGNORE_WCASTQUAL_END

macro: kill

#define  kill(pid, signal) kill(pid / 1024, signal)

macro: PLATFORM_LONG_OPTIONS

#define PLATFORM_LONG_OPTIONS

struct: envAccum

typedef struct envAccum_ {
   size_t capacity;
   size_t size;
   size_t bytes;
   char* env;
} envAccum;

typedef: kvar_t

typedef struct var kvar_t;

ProcessField.h

solaris/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   ZONEID = 100,                 \
   ZONE  = 101,                  \
   PROJID = 102,                 \
   TASKID = 103,                 \
   POOLID = 104,                 \
   CONTID = 105,                 \
   LWPID = 106,                  \
                                 \
   DUMMY_BUMP_FIELD = CWD,       \
   // End of list

SolarisMachine.c

solaris/SolarisMachine.c

function: Machine_delete

void Machine_delete(Machine* super) {
   SolarisMachine* this = (SolarisMachine*) super;

   Machine_done(super);

   free(this->cpus);
   if (this->kd) {
      kstat_close(this->kd);
   }
   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* super, unsigned int id) {
   assert(id < super->existingCPUs);

   const SolarisMachine* this = (const SolarisMachine*) super;

   return (super->existingCPUs == 1) ? true : this->cpus[id + 1].online;
}

function: Machine_new

Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
   SolarisMachine* this = xCalloc(1, sizeof(SolarisMachine));
   Machine* super = &this->super;

   Machine_init(super, usersTable, userId);

   this->pageSize = sysconf(_SC_PAGESIZE);
   if (this->pageSize == -1)
      CRT_fatalError("Cannot get pagesize by sysconf(_SC_PAGESIZE)");
   this->pageSizeKB = this->pageSize / 1024;

   this->kd = kstat_open();
   if (!this->kd)
      CRT_fatalError("Cannot open kstat handle");

   SolarisMachine_updateCPUcount(this);

   return super;
}

function: Machine_scan

void Machine_scan(Machine* super) {
   SolarisMachine* this = (SolarisMachine*) super;

   SolarisMachine_updateCPUcount(this);
   SolarisMachine_scanCPUTime(this);
   SolarisMachine_scanMemoryInfo(this);
   SolarisMachine_scanZfsArcstats(this);
}

function: SolarisMachine_scanCPUTime

function: SolarisMachine_scanMemoryInfo

function: SolarisMachine_scanZfsArcstats

static void SolarisMachine_scanZfsArcstats(SolarisMachine* this) {
   kstat_named_t       *cur_kstat;
   kstat_t             *arcstats;
   int                 ksrphyserr;

   if (this->kd == NULL)
      return;

   arcstats = kstat_lookup_wrapper(this->kd, "zfs", 0, "arcstats");
   if (arcstats == NULL)
      return;

   ksrphyserr = kstat_read(this->kd, arcstats, NULL);
   if (ksrphyserr == -1)
      return;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "size" );
   this->zfs.size = cur_kstat->value.ui64 / 1024;
   this->zfs.enabled = this->zfs.size > 0 ? 1 : 0;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "c_max" );
   this->zfs.max = cur_kstat->value.ui64 / 1024;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "mfu_size" );
   this->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "mru_size" );
   this->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "anon_size" );
   this->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "hdr_size" );
   this->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;

   cur_kstat = kstat_data_lookup_wrapper( arcstats, "other_size" );
   this->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;

   if ((cur_kstat = kstat_data_lookup_wrapper( arcstats, "compressed_size" )) != NULL) {
      this->zfs.compressed = cur_kstat->value.ui64 / 1024;
      this->zfs.isCompressed = 1;

      cur_kstat = kstat_data_lookup_wrapper( arcstats, "uncompressed_size" );
      this->zfs.uncompressed = cur_kstat->value.ui64 / 1024;
   } else {
      this->zfs.isCompressed = 0;
   }
}

function: SolarisMachine_updateCPUcount

static void SolarisMachine_updateCPUcount(SolarisMachine* this) {
   Machine* super = &this->super;
   long int s;
   bool change = false;

   s = sysconf(_SC_NPROCESSORS_CONF);
   if (s < 1)
      CRT_fatalError("Cannot get existing CPU count by sysconf(_SC_NPROCESSORS_CONF)");

   if (s != super->existingCPUs) {
      if (s == 1) {
         this->cpus = xRealloc(this->cpus, sizeof(CPUData));
         this->cpus[0].online = true;
      } else {
         this->cpus = xReallocArray(this->cpus, s + 1, sizeof(CPUData));
         this->cpus[0].online = true; /* average is always "online" */
         for (int i = 1; i < s + 1; i++) {
            this->cpus[i].online = false;
         }
      }

      change = true;
      super->existingCPUs = s;
   }

   s = sysconf(_SC_NPROCESSORS_ONLN);
   if (s < 1)
      CRT_fatalError("Cannot get active CPU count by sysconf(_SC_NPROCESSORS_ONLN)");

   if (s != super->activeCPUs) {
      change = true;
      super->activeCPUs = s;
   }

   if (change) {
      kstat_close(this->kd);
      this->kd = kstat_open();
      if (!this->kd)
         CRT_fatalError("Cannot open kstat handle");
   }
}

SolarisMachine.h

solaris/SolarisMachine.h

struct: CPUData

typedef struct CPUData_ {
   double userPercent;
   double nicePercent;
   double systemPercent;
   double irqPercent;
   double idlePercent;
   double systemAllPercent;
   double frequency;
   uint64_t luser;
   uint64_t lkrnl;
   uint64_t lintr;
   uint64_t lidle;
   bool online;
} CPUData;

struct: SolarisMachine

typedef struct SolarisMachine_ {
   Machine super;

   kstat_ctl_t* kd;
   CPUData* cpus;

   int pageSize;
   int pageSizeKB;

   ZfsArcStats zfs;
} SolarisMachine;

variable: zone_errmsg

extern char zone_errmsg[ZONE_ERRMSGLEN];

SolarisProcess.c

solaris/SolarisProcess.c

Function: SolarisProcess_new

Process* SolarisProcess_new(const Machine* host) {
   SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess));
   Object_setClass(this, Class(SolarisProcess));
   Process_init(&this->super, host);
   return (Process*)this;
}

Global Constant Array: Process_fields

Global Constant Structure (ProcessClass): SolarisProcess_class

const ProcessClass SolarisProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = SolarisProcess_rowWriteField
   },
   .compareByKey = SolarisProcess_compareByKey
};

Method: Process_delete

void Process_delete(Object* cast) {
   SolarisProcess* sp = (SolarisProcess*) cast;
   Process_done((Process*)cast);
   free(sp->zname);
   free(sp);
}

Static Method: SolarisProcess_compareByKey

static int SolarisProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const SolarisProcess* p1 = (const SolarisProcess*)v1;
   const SolarisProcess* p2 = (const SolarisProcess*)v2;

   switch (key) {
   case ZONEID:
      return SPACESHIP_NUMBER(p1->zoneid, p2->zoneid);
   case PROJID:
      return SPACESHIP_NUMBER(p1->projid, p2->projid);
   case TASKID:
      return SPACESHIP_NUMBER(p1->taskid, p2->taskid);
   case POOLID:
      return SPACESHIP_NUMBER(p1->poolid, p2->poolid);
   case CONTID:
      return SPACESHIP_NUMBER(p1->contid, p2->contid);
   case ZONE:
      return strcmp(p1->zname ? p1->zname : "global", p2->zname ? p2->zname : "global");
   case PID:
      return SPACESHIP_NUMBER(p1->realpid, p2->realpid);
   case PPID:
      return SPACESHIP_NUMBER(p1->realppid, p2->realppid);
   case LWPID:
      return SPACESHIP_NUMBER(p1->lwpid, p2->lwpid);
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

Static Method: SolarisProcess_rowWriteField

static void SolarisProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const SolarisProcess* sp = (const SolarisProcess*) super;

   char buffer[256]; buffer[255] = '\0';
   int attr = CRT_colors[DEFAULT_COLOR];
   size_t n = sizeof(buffer) - 1;

   switch (field) {
   // add Solaris-specific fields here
   case ZONEID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->zoneid); break;
   case PROJID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->projid); break;
   case TASKID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->taskid); break;
   case POOLID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->poolid); break;
   case CONTID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->contid); break;
   case ZONE: Row_printLeftAlignedField(str, attr, sp->zname ? sp->zname : "global", ZONENAME_MAX/4); return;
   case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realpid); break;
   case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realppid); break;
   case TGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->realtgid); break;
   case LWPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, sp->lwpid); break;
   default:
      Process_writeField(&sp->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

SolarisProcess.h

solaris/SolarisProcess.h

function: Process_delete

void Process_delete(Object* cast);

function: SolarisProcess_new

Process* SolarisProcess_new(const Machine* host);

struct: SolarisProcess

typedef struct SolarisProcess_ {
   Process    super;
   zoneid_t   zoneid;
   char*      zname;
   taskid_t   taskid;
   projid_t   projid;
   poolid_t   poolid;
   ctid_t     contid;
   pid_t      realpid;
   pid_t      realppid;
   pid_t      realtgid;
   pid_t      lwpid;
} SolarisProcess;

variable: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

variable: SolarisProcess_class

extern const ProcessClass SolarisProcess_class;

SolarisProcessTable.c

solaris/SolarisProcessTable.c

constant: GZONE

#define GZONE "global    "

constant: UZONE

#define UZONE "unknown   "

function: ProcessTable_delete

void ProcessTable_delete(Object* cast) {
   SolarisProcessTable* this = (SolarisProcessTable*) cast;
   ProcessTable_done(&this->super);
   free(this);
}

function: ProcessTable_goThroughEntries

void ProcessTable_goThroughEntries(ProcessTable* super) {
   super->kernelThreads = 1;
   proc_walk(&SolarisProcessTable_walkproc, super, PR_WALK_LWP);
}

function: ProcessTable_new

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
   SolarisProcessTable* this = xCalloc(1, sizeof(SolarisProcessTable));
   Object_setClass(this, Class(ProcessTable));

   ProcessTable* super = &this->super;
   ProcessTable_init(super, Class(SolarisProcess), host, pidMatchList);

   return super;
}

function: SolarisProcessTable_getProcessState

static inline ProcessState SolarisProcessTable_getProcessState(char state) {
   switch (state) {
      case 'S': return SLEEPING;
      case 'R': return RUNNABLE;
      case 'O': return RUNNING;
      case 'Z': return ZOMBIE;
      case 'T': return STOPPED;
      case 'I': return IDLE;
      default: return UNKNOWN;
   }
}

function: SolarisProcessTable_readZoneName

static char* SolarisProcessTable_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) {
   char* zname;

   if ( sproc->zoneid == 0 ) {
      zname = xStrdup(GZONE);
   } else if ( kd == NULL ) {
      zname = xStrdup(UZONE);
   } else {
      kstat_t* ks = kstat_lookup_wrapper( kd, "zones", sproc->zoneid, NULL );
      zname = xStrdup(ks == NULL ? UZONE : ks->ks_name);
   }

   return zname;
}

function: SolarisProcessTable_updateCwd

static void SolarisProcessTable_updateCwd(pid_t pid, Process* proc) {
   char path[32];
   xSnprintf(path, sizeof(path), "/proc/%d/cwd", pid);

   char target[PATH_MAX];
   ssize_t ret = readlink(path, target, sizeof(target) - 1);
   if (ret <= 0)
      return;

   target[ret] = '\0';
   free_and_xStrdup(&proc->procCwd, target);
}

function: SolarisProcessTable_updateExe

static void SolarisProcessTable_updateExe(pid_t pid, Process* proc) {
   char path[32];
   xSnprintf(path, sizeof(path), "/proc/%d/path/a.out", pid);

   char target[PATH_MAX];
   ssize_t ret = readlink(path, target, sizeof(target) - 1);
   if (ret <= 0)
      return;

   target[ret] = '\0';
   Process_updateExe(proc, target);
}

function: SolarisProcessTable_walkproc

SolarisProcessTable.h

solaris/SolarisProcessTable.h

struct: SolarisProcessTable

typedef struct SolarisProcessTable_ {
   ProcessTable super;
} SolarisProcessTable;

SwapMeter.c

SwapMeter.c

function: SwapMeter_display

static void SwapMeter_display(const Object* cast, RichString* out) {
   char buffer[50];
   const Meter* this = (const Meter*)cast;
   RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
   Meter_humanUnit(buffer, this->total, sizeof(buffer));
   RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
   Meter_humanUnit(buffer, this->values[SWAP_METER_USED], sizeof(buffer));
   RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:");
   RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);

   if (isNonnegative(this->values[SWAP_METER_CACHE])) {
      Meter_humanUnit(buffer, this->values[SWAP_METER_CACHE], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:");
      RichString_appendAscii(out, CRT_colors[SWAP_CACHE], buffer);
   }

   if (isNonnegative(this->values[SWAP_METER_FRONTSWAP])) {
      Meter_humanUnit(buffer, this->values[SWAP_METER_FRONTSWAP], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " frontswap:");
      RichString_appendAscii(out, CRT_colors[SWAP_FRONTSWAP], buffer);
   }
}

function: SwapMeter_updateValues

static void SwapMeter_updateValues(Meter* this) {
   char* buffer = this->txtBuffer;
   size_t size = sizeof(this->txtBuffer);
   int written;

   this->values[SWAP_METER_CACHE] = NAN;   /* 'cached' not present on all platforms */
   this->values[SWAP_METER_FRONTSWAP] = NAN;   /* 'frontswap' not present on all platforms */
   Platform_setSwapValues(this);

   written = Meter_humanUnit(buffer, this->values[SWAP_METER_USED], size);
   METER_BUFFER_CHECK(buffer, size, written);

   METER_BUFFER_APPEND_CHR(buffer, size, '/');

   Meter_humanUnit(buffer, this->total, size);
}

global_variable: SwapMeter_attributes

static const int SwapMeter_attributes[] = {
   SWAP,
   SWAP_CACHE,
   SWAP_FRONTSWAP,
};

struct: SwapMeter_class

const MeterClass SwapMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = SwapMeter_display,
   },
   .updateValues = SwapMeter_updateValues,
   .defaultMode = BAR_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = SWAP_METER_ITEMCOUNT,
   .total = 100.0,
   .attributes = SwapMeter_attributes,
   .name = "Swap",
   .uiName = "Swap",
   .caption = "Swp"
};

SwapMeter.h

SwapMeter.h

enum: SwapMeterValues

typedef enum {
   SWAP_METER_USED = 0,
   SWAP_METER_CACHE = 1,
   SWAP_METER_FRONTSWAP = 2,
   SWAP_METER_ITEMCOUNT = 3, // number of entries in this enum
} SwapMeterValues;

variable: SwapMeter_class

extern const MeterClass SwapMeter_class;

SysArchMeter.c

SysArchMeter.c

function: SysArchMeter_updateValues

static void SysArchMeter_updateValues(Meter* this) {
   static char* string;

   if (string == NULL)
      Platform_getRelease(&string);

   String_safeStrncpy(this->txtBuffer, string, sizeof(this->txtBuffer));
}

global struct: SysArchMeter_class

const MeterClass SysArchMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = SysArchMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = SysArchMeter_attributes,
   .name = "System",
   .uiName = "System",
   .caption = "System: ",
};

global variable: SysArchMeter_attributes

static const int SysArchMeter_attributes[] = {HOSTNAME};

SysArchMeter.h

SysArchMeter.h

struct: SysArchMeter_class

extern const MeterClass SysArchMeter_class;

Table.c

Table.c

function: compareRowByKnownParentThenNatural

static int compareRowByKnownParentThenNatural(const void* v1, const void* v2) {
   return Row_compareByParent((const Row*) v1, (const Row*) v2);
}

function: Table_add

void Table_add(Table* this, Row* row) {
   assert(Vector_indexOf(this->rows, row, Row_idEqualCompare) == -1);
   assert(Hashtable_get(this->table, row->id) == NULL);

   // highlighting row found in first scan by first scan marked "far in the past"
   row->seenStampMs = this->host->monotonicMs;

   Vector_add(this->rows, row);
   Hashtable_put(this->table, row->id, row);

   assert(Vector_indexOf(this->rows, row, Row_idEqualCompare) != -1);
   assert(Hashtable_get(this->table, row->id) != NULL);
   assert(Vector_countEquals(this->rows, Hashtable_count(this->table)));
}

function: Table_buildTree

static void Table_buildTree(Table* this) {
   Vector_prune(this->displayList);

   // Mark root processes
   int vsize = Vector_size(this->rows);
   for (int i = 0; i < vsize; i++) {
      Row* row = (Row*) Vector_get(this->rows, i);
      int parent = Row_getGroupOrParent(row);
      row->isRoot = false;

      if (row->id == parent) {
         row->isRoot = true;
         continue;
      }

      if (!parent) {
         row->isRoot = true;
         continue;
      }

      // We don't know about its parent for whatever reason
      if (Table_findRow(this, parent) == NULL)
         row->isRoot = true;
   }

   // Sort by known parent (roots first), then row ID
   Vector_quickSortCustomCompare(this->rows, compareRowByKnownParentThenNatural);

   // Find all processes whose parent is not visible
   for (int i = 0; i < vsize; i++) {
      Row* row = (Row*)Vector_get(this->rows, i);

      // If parent not found, then construct the tree with this node as root
      if (row->isRoot) {
         row = (Row*)Vector_get(this->rows, i);
         row->indent = 0;
         row->tree_depth = 0;
         Vector_add(this->displayList, row);
         Table_buildTreeBranch(this, row->id, 0, 0, row->showChildren);
         continue;
      }
   }

   this->needsSort = false;

   // Check consistency of the built structures
   assert(Vector_size(this->displayList) == vsize); (void)vsize;
}

function: Table_buildTreeBranch

static void Table_buildTreeBranch(Table* this, int rowid, unsigned int level, int32_t indent, bool show) {
   // Do not treat zero as root of any tree.
   // (e.g. on OpenBSD the kernel thread 'swapper' has pid 0.)
   if (rowid == 0)
      return;

   // The vector is sorted by parent, find the start of the range by bisection
   int vsize = Vector_size(this->rows);
   int l = 0;
   int r = vsize;
   while (l < r) {
      int c = (l + r) / 2;
      Row* row = (Row*)Vector_get(this->rows, c);
      int parent = row->isRoot ? 0 : Row_getGroupOrParent(row);
      if (parent < rowid) {
         l = c + 1;
      } else {
         r = c;
      }
   }
   // Find the end to know the last line for indent handling purposes
   int lastShown = r;
   while (r < vsize) {
      Row* row = (Row*)Vector_get(this->rows, r);
      if (!Row_isChildOf(row, rowid))
         break;
      if (row->show)
         lastShown = r;
      r++;
   }

   for (int i = l; i < r; i++) {
      Row* row = (Row*)Vector_get(this->rows, i);

      if (!show)
         row->show = false;

      Vector_add(this->displayList, row);

      int32_t nextIndent = indent | ((int32_t)1 << MINIMUM(level, sizeof(row->indent) * 8 - 2));
      Table_buildTreeBranch(this, row->id, level + 1, (i < lastShown) ? nextIndent : indent, row->show && row->showChildren);
      if (i == lastShown)
         row->indent = -nextIndent;
      else
         row->indent = nextIndent;

      row->tree_depth = level + 1;
   }
}

function: Table_cleanupEntries

void Table_cleanupEntries(Table* this) {
   // Finish process table update, culling any removed rows
   for (int i = Vector_size(this->rows) - 1; i >= 0; i--) {
      Row* row = (Row*) Vector_get(this->rows, i);
      Table_cleanupRow(this, row, i);
   }

   // compact the table in case of any earlier row removals
   Table_compact(this);
}

function: Table_cleanupRow

void Table_cleanupRow(Table* table, Row* row, int idx) {
   Machine* host = table->host;
   const Settings* settings = host->settings;

   if (row->tombStampMs > 0) {
      // remove tombed process
      if (host->monotonicMs >= row->tombStampMs) {
         Table_removeIndex(table, row, idx);
      }
   } else if (row->updated == false) {
      // process no longer exists
      if (settings->highlightChanges && row->wasShown) {
         // mark tombed
         row->tombStampMs = host->monotonicMs + 1000 * settings->highlightDelaySecs;
      } else {
         // immediately remove
         Table_removeIndex(table, row, idx);
      }
   }
}

function: Table_collapseAllBranches

void Table_collapseAllBranches(Table* this) {
   Table_buildTree(this); // Update `tree_depth` fields of the rows
   this->needsSort = true; // Table is sorted by parent now, force new sort
   int size = Vector_size(this->rows);
   for (int i = 0; i < size; i++) {
      Row* row = (Row*) Vector_get(this->rows, i);
      // FreeBSD has pid 0 = kernel and pid 1 = init, so init has tree_depth = 1
      if (row->tree_depth > 0 && row->id > 1)
         row->showChildren = false;
   }
}

function: Table_delete

static void Table_delete(Object* cast) {
   Table* this = (Table*) cast;
   Table_done(this);
   free(this);
}

function: Table_done

void Table_done(Table* this) {
   Hashtable_delete(this->table);
   Vector_delete(this->displayList);
   Vector_delete(this->rows);
}

function: Table_expandTree

void Table_expandTree(Table* this) {
   int size = Vector_size(this->rows);
   for (int i = 0; i < size; i++) {
      Row* row = (Row*) Vector_get(this->rows, i);
      row->showChildren = true;
   }
}

function: Table_init

Table* Table_init(Table* this, const ObjectClass* klass, Machine* host) {
   this->rows = Vector_new(klass, true, DEFAULT_SIZE);
   this->displayList = Vector_new(klass, false, DEFAULT_SIZE);
   this->table = Hashtable_new(200, false);
   this->needsSort = true;
   this->following = -1;
   this->host = host;
   return this;
}

function: Table_prepareEntries

void Table_prepareEntries(Table* this) {
   for (int i = 0; i < Vector_size(this->rows); i++) {
      Row* row = (struct Row_*) Vector_get(this->rows, i);
      row->updated = false;
      row->wasShown = row->show;
      row->show = true;
   }
}

function: Table_printHeader

void Table_printHeader(const Settings* settings, RichString* header) {
   RichString_rewind(header, RichString_size(header));

   const ScreenSettings* ss = settings->ss;
   const RowField* fields = ss->fields;

   RowField key = ScreenSettings_getActiveSortKey(ss);

   for (int i = 0; fields[i]; i++) {
      int color;
      if (ss->treeView && ss->treeViewAlwaysByPID) {
         color = CRT_colors[PANEL_HEADER_FOCUS];
      } else if (key == fields[i]) {
         color = CRT_colors[PANEL_SELECTION_FOCUS];
      } else {
         color = CRT_colors[PANEL_HEADER_FOCUS];
      }

      RichString_appendWide(header, color, RowField_alignedTitle(settings, fields[i]));
      if (key == fields[i] && RichString_getCharVal(*header, RichString_size(header) - 1) == ' ') {
         bool ascending = ScreenSettings_getActiveDirection(ss) == 1;
         RichString_rewind(header, 1);  // rewind to override space
         RichString_appendWide(header,
                                 CRT_colors[PANEL_SELECTION_FOCUS],
                                 CRT_treeStr[ascending ? TREE_STR_ASC : TREE_STR_DESC]);
      }
      if (COMM == fields[i] && settings->showMergedCommand) {
         RichString_appendAscii(header, color, "(merged)");
      }
   }
}

function: Table_rebuildPanel

void Table_rebuildPanel(Table* this) {
   Table_updateDisplayList(this);

   const int currPos = Panel_getSelectedIndex(this->panel);
   const int currScrollV = this->panel->scrollV;
   const int currSize = Panel_size(this->panel);

   Panel_prune(this->panel);

   /* Follow main group row instead if following a row that is occluded (hidden) */
   if (this->following != -1) {
      const Row* followed = (const Row*) Hashtable_get(this->table, this->following);
      if (followed != NULL
         && Hashtable_get(this->table, followed->group)
         && Row_isVisible(followed, this) == false ) {
         this->following = followed->group;
      }
   }

   const int rowCount = Vector_size(this->displayList);
   bool foundFollowed = false;
   int idx = 0;

   for (int i = 0; i < rowCount; i++) {
      Row* row = (Row*) Vector_get(this->displayList, i);

      if ( !row->show || (Row_matchesFilter(row, this) == true) )
         continue;

      Panel_set(this->panel, idx, (Object*)row);

      if (this->following != -1 && row->id == this->following) {
         foundFollowed = true;
         Panel_setSelected(this->panel, idx);
         /* Keep scroll position relative to followed row */
         this->panel->scrollV = idx - (currPos - currScrollV);
      }
      idx++;
   }

   if (this->following != -1 && !foundFollowed) {
      /* Reset if current followed row not found */
      this->following = -1;
      Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
   }

   if (this->following == -1) {
      /* If the last item was selected, keep the new last item selected */
      if (currPos > 0 && currPos == currSize - 1)
         Panel_setSelected(this->panel, Panel_size(this->panel) - 1);
      else
         Panel_setSelected(this->panel, currPos);

      this->panel->scrollV = currScrollV;
   }
}

function: Table_removeIndex

static void Table_removeIndex(Table* this, const Row* row, int idx) {
   int rowid = row->id;

   assert(row == (Row*)Vector_get(this->rows, idx));
   assert(Hashtable_get(this->table, rowid) != NULL);

   Hashtable_remove(this->table, rowid);
   Vector_softRemove(this->rows, idx);

   if (this->following != -1 && this->following == rowid) {
      this->following = -1;
      Panel_setSelectionColor(this->panel, PANEL_SELECTION_FOCUS);
   }

   assert(Hashtable_get(this->table, rowid) == NULL);
   assert(Vector_countEquals(this->rows, Hashtable_count(this->table)));
}

function: Table_setPanel

void Table_setPanel(Table* this, Panel* panel) {
   this->panel = panel;
}

function: Table_updateDisplayList

void Table_updateDisplayList(Table* this) {
   const Settings* settings = this->host->settings;

   if (settings->ss->treeView) {
      if (this->needsSort)
         Table_buildTree(this);
   } else {
      if (this->needsSort)
         Vector_insertionSort(this->rows);
      Vector_prune(this->displayList);
      int size = Vector_size(this->rows);
      for (int i = 0; i < size; i++)
         Vector_add(this->displayList, Vector_get(this->rows, i));
   }
   this->needsSort = false;
}

struct: Table_class

const TableClass Table_class = {
   .super = {
      .extends = Class(Object),
      .delete = Table_delete,
   },
   .prepare = Table_prepareEntries,
   .cleanup = Table_cleanupEntries,
};

Table.h

Table.h

extern const: Table_class

extern const TableClass Table_class;

function: Table_add

void Table_add(Table* this, struct Row_* row);

function: Table_cleanupEntries

void Table_cleanupEntries(Table* this);

function: Table_cleanupRow

void Table_cleanupRow(Table* this, Row* row, int idx);

function: Table_collapseAllBranches

void Table_collapseAllBranches(Table* this);

function: Table_compact

static inline void Table_compact(Table* this) {
   Vector_compact(this->rows);
   this->needsSort = true;
}

function: Table_done

void Table_done(Table* this);

function: Table_expandTree

void Table_expandTree(Table* this);

function: Table_findRow

static inline struct Row_* Table_findRow(Table* this, int id) {
   return (struct Row_*) Hashtable_get(this->table, id);
}

function: Table_init

Table* Table_init(Table* this, const ObjectClass* klass, struct Machine_* host);

function: Table_prepareEntries

void Table_prepareEntries(Table* this);

function: Table_printHeader

void Table_printHeader(const Settings* settings, RichString* header);

function: Table_rebuildPanel

void Table_rebuildPanel(Table* this);

function: Table_setPanel

void Table_setPanel(Table* this, struct Panel_* panel);

function: Table_updateDisplayList

void Table_updateDisplayList(Table* this);

macro: As_Table

#define As_Table(this_)  ((const TableClass*)((this_)->super.klass))

macro: Table_scanCleanup

#define Table_scanCleanup(t_)  (As_Table(t_)->cleanup ? (As_Table(t_)->cleanup(t_)) : Table_cleanupEntries(t_))

macro: Table_scanIterate

#define Table_scanIterate(t_)  (As_Table(t_)->iterate(t_))  /* mandatory; must have a custom iterate method */

macro: Table_scanPrepare

#define Table_scanPrepare(t_)  (As_Table(t_)->prepare ? (As_Table(t_)->prepare(t_)) : Table_prepareEntries(t_))

struct: Table

typedef struct Table_ {
   /* Super object for emulated OOP */
   Object super;

   Vector* rows;          /* all known; sort order can vary and differ from display order */
   Vector* displayList;   /* row tree flattened in display order (borrowed);
                             updated in Table_updateDisplayList when rebuilding panel */
   Hashtable* table;     /* fast known row lookup by identifier */

   struct Machine_* host;
   const char* incFilter;
   bool needsSort;
   int following;         /* -1 or row being visually tracked in the user interface */

   struct Panel_* panel;
} Table;

struct: TableClass

typedef struct TableClass_ {
   const ObjectClass super;
   const Table_ScanPrepare prepare;
   const Table_ScanIterate iterate;
   const Table_ScanCleanup cleanup;
} TableClass;

typedef: Table_New

typedef Table* (*Table_New)(const struct Machine_*);

typedef: Table_ScanCleanup

typedef void (*Table_ScanCleanup)(Table* this);

typedef: Table_ScanIterate

typedef void (*Table_ScanIterate)(Table* this);

typedef: Table_ScanPrepare

typedef void (*Table_ScanPrepare)(Table* this);

TasksMeter.c

TasksMeter.c

function: TasksMeter_display

static void TasksMeter_display(const Object* cast, RichString* out) {
   const Meter* this = (const Meter*)cast;
   const Settings* settings = this->host->settings;
   char buffer[20];
   int len;

   len = xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[2]);
   RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, len);

   RichString_appendAscii(out, settings->hideUserlandThreads ? CRT_colors[METER_SHADOW] : CRT_colors[METER_TEXT], ", ");
   len = xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[1]);
   RichString_appendnAscii(out, settings->hideUserlandThreads ? CRT_colors[METER_SHADOW] : CRT_colors[TASKS_RUNNING], buffer, len);
   RichString_appendAscii(out, settings->hideUserlandThreads ? CRT_colors[METER_SHADOW] : CRT_colors[METER_TEXT], " thr");

   RichString_appendAscii(out, settings->hideKernelThreads ? CRT_colors[METER_SHADOW] : CRT_colors[METER_TEXT], ", ");
   len = xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[0]);
   RichString_appendnAscii(out, settings->hideKernelThreads ? CRT_colors[METER_SHADOW] : CRT_colors[TASKS_RUNNING], buffer, len);
   RichString_appendAscii(out, settings->hideKernelThreads ? CRT_colors[METER_SHADOW] : CRT_colors[METER_TEXT], " kthr");

   RichString_appendAscii(out, CRT_colors[METER_TEXT], "; ");
   len = xSnprintf(buffer, sizeof(buffer), "%d", (int)this->values[3]);
   RichString_appendnAscii(out, CRT_colors[TASKS_RUNNING], buffer, len);
   RichString_appendAscii(out, CRT_colors[METER_TEXT], " running");
}

function: TasksMeter_updateValues

static void TasksMeter_updateValues(Meter* this) {
   const Machine* host = this->host;
   const ProcessTable* pt = (const ProcessTable*) host->processTable;

   this->values[0] = pt->kernelThreads;
   this->values[1] = pt->userlandThreads;
   this->values[2] = pt->totalTasks - pt->kernelThreads - pt->userlandThreads;
   this->values[3] = MINIMUM(pt->runningTasks, host->activeCPUs);
   this->total     = pt->totalTasks;

   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%u/%u", MINIMUM(pt->runningTasks, host->activeCPUs), pt->totalTasks);
}

struct: TasksMeter_class

const MeterClass TasksMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = TasksMeter_display,
   },
   .updateValues = TasksMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 4,
   .total = 100.0,
   .attributes = TasksMeter_attributes,
   .name = "Tasks",
   .uiName = "Task counter",
   .caption = "Tasks: "
};

variable: TasksMeter_attributes

static const int TasksMeter_attributes[] = {
   CPU_SYSTEM,
   PROCESS_THREAD,
   PROCESS,
   TASKS_RUNNING
};

TasksMeter.h

TasksMeter.h

external constant variable: TasksMeter_class

extern const MeterClass TasksMeter_class;

test_spec.lua

test_spec.lua

file: test_spec.lua

TESTPLAN

TESTPLAN

file: TESTPLAN

TraceScreen.c

TraceScreen.c

array: TraceScreenEvents

static const int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};

array: TraceScreenFunctions

static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing   ", "Done   ", NULL};

array: TraceScreenKeys

static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};

function: TraceScreen_delete

void TraceScreen_delete(Object* cast) {
   TraceScreen* this = (TraceScreen*) cast;
   if (this->child > 0) {
      kill(this->child, SIGTERM);
      while (waitpid(this->child, NULL, 0) == -1)
         if (errno != EINTR)
            break;
   }

   if (this->strace) {
      fclose(this->strace);
   }

   CRT_enableDelay();
   free(InfoScreen_done((InfoScreen*)this));
}

function: TraceScreen_draw

static void TraceScreen_draw(InfoScreen* this) {
   InfoScreen_drawTitled(this, "Trace of process %d - %s", Process_getPid(this->process), Process_getCommand(this->process));
}

function: TraceScreen_forkTracer

function: TraceScreen_new

TraceScreen* TraceScreen_new(const Process* process) {
   // This initializes all TraceScreen variables to "false" so only default = true ones need to be set below
   TraceScreen* this = xCalloc(1, sizeof(TraceScreen));
   Object_setClass(this, Class(TraceScreen));
   this->tracing = true;
   this->strace_alive = false;
   FunctionBar* fuBar = FunctionBar_new(TraceScreenFunctions, TraceScreenKeys, TraceScreenEvents);
   CRT_disableDelay();
   return (TraceScreen*) InfoScreen_init(&this->super, process, fuBar, LINES - 2, " ");
}

function: TraceScreen_onKey

static bool TraceScreen_onKey(InfoScreen* super, int ch) {
   TraceScreen* this = (TraceScreen*) super;

   switch (ch) {
      case 'f':
      case KEY_F(8):
         this->follow = !(this->follow);
         if (this->follow)
            Panel_setSelected(super->display, Panel_size(super->display) - 1);
         return true;
      case 't':
      case KEY_F(9):
         this->tracing = !this->tracing;
         FunctionBar_setLabel(super->display->defaultBar, KEY_F(9), this->tracing ? "Stop Tracing   " : "Resume Tracing ");
         InfoScreen_draw(this);
         return true;
   }

   this->follow = false;
   return false;
}

function: TraceScreen_updateTrace

static void TraceScreen_updateTrace(InfoScreen* super) {
   TraceScreen* this = (TraceScreen*) super;

   int fd_strace = fileno(this->strace);

   fd_set fds;
   FD_ZERO(&fds);
   FD_SET(STDIN_FILENO, &fds);
   if (this->strace_alive) {
      assert(fd_strace != -1);
      FD_SET(fd_strace, &fds);
   }

   struct timeval tv = { .tv_sec = 0, .tv_usec = 500 };
   int ready = select(MAXIMUM(STDIN_FILENO, fd_strace) + 1, &fds, NULL, NULL, &tv);

   char buffer[1025];
   size_t nread = 0;
   if (ready > 0 && FD_ISSET(fd_strace, &fds))
      nread = fread(buffer, 1, sizeof(buffer) - 1, this->strace);

   if (nread && this->tracing) {
      const char* line = buffer;
      buffer[nread] = '\0';
      for (size_t i = 0; i < nread; i++) {
         if (buffer[i] == '\n') {
            buffer[i] = '\0';
            if (this->contLine) {
               InfoScreen_appendLine(&this->super, line);
               this->contLine = false;
            } else {
               InfoScreen_addLine(&this->super, line);
            }
            line = buffer + i + 1;
         }
      }
      if (line < buffer + nread) {
         InfoScreen_addLine(&this->super, line);
         buffer[nread] = '\0';
         this->contLine = true;
      }
      if (this->follow) {
         Panel_setSelected(this->super.display, Panel_size(this->super.display) - 1);
      }
   } else {
      if (this->strace_alive && waitpid(this->child, NULL, WNOHANG) != 0)
         this->strace_alive = false;
   }
}

struct: TraceScreen_class

const InfoScreenClass TraceScreen_class = {
   .super = {
      .extends = Class(Object),
      .delete = TraceScreen_delete
   },
   .draw = TraceScreen_draw,
   .onErr = TraceScreen_updateTrace,
   .onKey = TraceScreen_onKey,
};

TraceScreen.h

TraceScreen.h

function: TraceScreen_delete

void TraceScreen_delete(Object* cast);

function: TraceScreen_forkTracer

bool TraceScreen_forkTracer(TraceScreen* this);

function: TraceScreen_new

TraceScreen* TraceScreen_new(const Process* process);

struct: TraceScreen

typedef struct TraceScreen_ {
   InfoScreen super;
   bool tracing;
   pid_t child;
   FILE* strace;
   bool contLine;
   bool follow;
   bool strace_alive;
} TraceScreen;

struct: TraceScreen_class

extern const InfoScreenClass TraceScreen_class;

Platform.c

unsupported/Platform.c

array: Platform_defaultScreens

const ScreenDefaults Platform_defaultScreens[] = {
   {
      .name = "Main",
      .columns = "PID USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
      .sortKey = "PERCENT_CPU",
   },
};

array: Platform_meterTypes

const MeterClass* const Platform_meterTypes[] = {
   &CPUMeter_class,
   &ClockMeter_class,
   &DateMeter_class,
   &DateTimeMeter_class,
   &LoadAverageMeter_class,
   &LoadMeter_class,
   &MemoryMeter_class,
   &SwapMeter_class,
   &MemorySwapMeter_class,
   &TasksMeter_class,
   &BatteryMeter_class,
   &HostnameMeter_class,
   &SysArchMeter_class,
   &UptimeMeter_class,
   &AllCPUsMeter_class,
   &AllCPUs2Meter_class,
   &AllCPUs4Meter_class,
   &AllCPUs8Meter_class,
   &LeftCPUsMeter_class,
   &RightCPUsMeter_class,
   &LeftCPUs2Meter_class,
   &RightCPUs2Meter_class,
   &LeftCPUs4Meter_class,
   &RightCPUs4Meter_class,
   &LeftCPUs8Meter_class,
   &RightCPUs8Meter_class,
   &FileDescriptorMeter_class,
   &BlankMeter_class,
   NULL
};

array: Platform_signals

const SignalItem Platform_signals[] = {
   { .name = " 0 Cancel",    .number =  0 },
};

function: Platform_done

void Platform_done(void) {
   /* no platform-specific cleanup needed */
}

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC) {
   *percent = NAN;
   *isOnAC = AC_ERROR;
}

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data) {
   (void)data;
   return false;
}

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max) {
   *used = 1337;
   *max = 4711;
}

function: Platform_getHostname

void Platform_getHostname(char* buffer, size_t size) {
   String_safeStrncpy(buffer, Platform_unsupported, size);
}

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
   *one = 0;
   *five = 0;
   *fifteen = 0;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void) {
   return INT_MAX;
}

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data) {
   (void)data;
   return false;
}

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid) {
   (void) pid;
   return NULL;
}

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
   (void)pid;
   return NULL;
}

function: Platform_getRelease

void Platform_getRelease(char** string) {
   *string = xStrdup(Platform_unsupported);
}

function: Platform_getUptime

int Platform_getUptime(void) {
   return 0;
}

function: Platform_init

bool Platform_init(void) {
   /* no platform-specific setup needed */
   return true;
}

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys) {
   /* no platform-specific key bindings */
   (void) keys;
}

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu) {
   (void) cpu;

   double* v = this->values;
   v[CPU_METER_FREQUENCY] = NAN;
   v[CPU_METER_TEMPERATURE] = NAN;

   this->curItems = 1;

   return 0.0;
}

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this) {
   (void) this;
}

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this) {
   (void) this;
}

variable: Platform_numberOfDefaultScreens

const unsigned int Platform_numberOfDefaultScreens = ARRAYSIZE(Platform_defaultScreens);

variable: Platform_numberOfSignals

const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);

variable: Platform_unsupported

static const char Platform_unsupported[] = "unsupported";

Platform.h

unsupported/Platform.h

function: Platform_addDynamicScreen

static inline void Platform_addDynamicScreen(ATTR_UNUSED ScreenSettings* ss) { }

function: Platform_addDynamicScreenAvailableColumns

static inline void Platform_addDynamicScreenAvailableColumns(ATTR_UNUSED Panel* availableColumns, ATTR_UNUSED const char* screen) { }

function: Platform_defaultDynamicScreens

static inline void Platform_defaultDynamicScreens(ATTR_UNUSED Settings* settings) { }

function: Platform_done

void Platform_done(void);

function: Platform_dynamicColumnName

static inline const char* Platform_dynamicColumnName(ATTR_UNUSED unsigned int key) {
   return NULL;
}

function: Platform_dynamicColumns

static inline Hashtable* Platform_dynamicColumns(void) {
   return NULL;
}

function: Platform_dynamicColumnsDone

static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicColumnWriteField

static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) {
   return false;
}

function: Platform_dynamicMeterDisplay

static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { }

function: Platform_dynamicMeterInit

static inline void Platform_dynamicMeterInit(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicMeters

static inline Hashtable* Platform_dynamicMeters(void) {
   return NULL;
}

function: Platform_dynamicMetersDone

static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { }

function: Platform_dynamicMeterUpdateValues

static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { }

function: Platform_dynamicScreens

static inline Hashtable* Platform_dynamicScreens(void) {
   return NULL;
}

function: Platform_dynamicScreensDone

static inline void Platform_dynamicScreensDone(ATTR_UNUSED Hashtable* screens) { }

function: Platform_getBattery

void Platform_getBattery(double* percent, ACPresence* isOnAC);

function: Platform_getDiskIO

bool Platform_getDiskIO(DiskIOData* data);

function: Platform_getFileDescriptors

void Platform_getFileDescriptors(double* used, double* max);

function: Platform_getHostname

void Platform_getHostname(char* buffer, size_t size);

function: Platform_getLoadAverage

void Platform_getLoadAverage(double* one, double* five, double* fifteen);

function: Platform_getLongOption

static inline CommandLineStatus Platform_getLongOption(ATTR_UNUSED int opt, ATTR_UNUSED int argc, ATTR_UNUSED char** argv) {
   return STATUS_ERROR_EXIT;
}

function: Platform_getMaxPid

pid_t Platform_getMaxPid(void);

function: Platform_getNetworkIO

bool Platform_getNetworkIO(NetworkIOData* data);

function: Platform_getProcessEnv

char* Platform_getProcessEnv(pid_t pid);

function: Platform_getProcessLocks

FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);

function: Platform_getRelease

void Platform_getRelease(char** string);

function: Platform_gettime_monotonic

static inline void Platform_gettime_monotonic(uint64_t* msec) {
   Generic_gettime_monotonic(msec);
}

function: Platform_gettime_realtime

static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
   Generic_gettime_realtime(tv, msec);
}

function: Platform_getUptime

int Platform_getUptime(void);

function: Platform_init

bool Platform_init(void);

function: Platform_longOptionsUsage

static inline void Platform_longOptionsUsage(ATTR_UNUSED const char* name) { }

function: Platform_setBindings

void Platform_setBindings(Htop_Action* keys);

function: Platform_setCPUValues

double Platform_setCPUValues(Meter* this, unsigned int cpu);

function: Platform_setMemoryValues

void Platform_setMemoryValues(Meter* this);

function: Platform_setSwapValues

void Platform_setSwapValues(Meter* this);

global variable: Platform_numberOfDefaultScreens

extern const unsigned int Platform_numberOfDefaultScreens;

global variable: Platform_numberOfSignals

extern const unsigned int Platform_numberOfSignals;

global variable (array of pointers): Platform_meterTypes

extern const MeterClass* const Platform_meterTypes[];

global variable (array): Platform_defaultScreens

extern const ScreenDefaults Platform_defaultScreens[];

global variable (array): Platform_signals

extern const SignalItem Platform_signals[];

macro: PLATFORM_LONG_OPTIONS

#define PLATFORM_LONG_OPTIONS

ProcessField.h

unsupported/ProcessField.h

macro: PLATFORM_PROCESS_FIELDS

#define PLATFORM_PROCESS_FIELDS  \
   // End of list

UnsupportedMachine.c

unsupported/UnsupportedMachine.c

function: Machine_delete

void Machine_delete(Machine* super) {
   UnsupportedMachine* this = (UnsupportedMachine*) super;
   Machine_done(super);
   free(this);
}

function: Machine_isCPUonline

bool Machine_isCPUonline(const Machine* host, unsigned int id) {
   assert(id < host->existingCPUs);

   (void) host; (void) id;

   return true;
}

function: Machine_new

Machine* Machine_new(UsersTable* usersTable, uid_t userId) {
   UnsupportedMachine* this = xCalloc(1, sizeof(UnsupportedMachine));
   Machine* super = &this->super;

   Machine_init(super, usersTable, userId);

   super->existingCPUs = 1;
   super->activeCPUs = 1;

   return super;
}

function: Machine_scan

void Machine_scan(Machine* super) {
   super->existingCPUs = 1;
   super->activeCPUs = 1;

   super->totalMem = 0;
   super->usedMem = 0;
   super->buffersMem = 0;
   super->cachedMem = 0;
   super->sharedMem = 0;
   super->availableMem = 0;

   super->totalSwap = 0;
   super->usedSwap = 0;
   super->cachedSwap = 0;
}

UnsupportedMachine.h

unsupported/UnsupportedMachine.h

struct: UnsupportedMachine

typedef struct UnsupportedMachine_ {
   Machine super;
} UnsupportedMachine;

UnsupportedProcess.c

unsupported/UnsupportedProcess.c

function: Process_delete

void Process_delete(Object* cast) {
   Process* super = (Process*) cast;
   Process_done(super);
   // free platform-specific fields here
   free(cast);
}

function: UnsupportedProcess_compareByKey

static int UnsupportedProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
   const UnsupportedProcess* p1 = (const UnsupportedProcess*)v1;
   const UnsupportedProcess* p2 = (const UnsupportedProcess*)v2;

   (void) p1;
   (void) p2;

   switch (key) {
   /* Add platform specific fields */
   default:
      return Process_compareByKey_Base(v1, v2, key);
   }
}

function: UnsupportedProcess_new

Process* UnsupportedProcess_new(const Machine* host) {
   Process* this = xCalloc(1, sizeof(UnsupportedProcess));
   Object_setClass(this, Class(UnsupportedProcess));
   Process_init(this, host);
   return this;
}

function: UnsupportedProcess_rowWriteField

static void UnsupportedProcess_rowWriteField(const Row* super, RichString* str, ProcessField field) {
   const UnsupportedProcess* up = (const UnsupportedProcess*) super;

   bool coloring = super->host->settings->highlightMegabytes;
   char buffer[256]; buffer[255] = '\0';
   int attr = CRT_colors[DEFAULT_COLOR];
   size_t n = sizeof(buffer) - 1;

   (void) coloring;
   (void) n;

   switch (field) {
   /* Add platform specific fields */
   default:
      Process_writeField(&up->super, str, field);
      return;
   }

   RichString_appendWide(str, attr, buffer);
}

global_variable: Process_fields

global_variable: UnsupportedProcess_class

const ProcessClass UnsupportedProcess_class = {
   .super = {
      .super = {
         .extends = Class(Process),
         .display = Row_display,
         .delete = Process_delete,
         .compare = Process_compare
      },
      .isHighlighted = Process_rowIsHighlighted,
      .isVisible = Process_rowIsVisible,
      .matchesFilter = Process_rowMatchesFilter,
      .compareByParent = Process_compareByParent,
      .sortKeyString = Process_rowGetSortKey,
      .writeField = UnsupportedProcess_rowWriteField
   },
   .compareByKey = UnsupportedProcess_compareByKey
};

UnsupportedProcess.h

unsupported/UnsupportedProcess.h

extern variable: Process_fields

extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD];

extern variable: UnsupportedProcess_class

extern const ProcessClass UnsupportedProcess_class;

function: Process_delete

void Process_delete(Object* cast);

function: UnsupportedProcess_new

Process* UnsupportedProcess_new(const Machine* host);

struct: UnsupportedProcess_

typedef struct UnsupportedProcess_ {
   Process super;

   /* Add platform specific fields */
} UnsupportedProcess;

UnsupportedProcessTable.c

unsupported/UnsupportedProcessTable.c

function: ProcessTable_delete

void ProcessTable_delete(Object* cast) {
   UnsupportedProcessTable* this = (UnsupportedProcessTable*) cast;
   ProcessTable_done(&this->super);
   free(this);
}

function: ProcessTable_goThroughEntries

void ProcessTable_goThroughEntries(ProcessTable* super) {
   bool preExisting = true;
   Process* proc;

   proc = ProcessTable_getProcess(super, 1, &preExisting, UnsupportedProcess_new);

   /* Empty values */
   proc->time = proc->time + 10;
   Process_setPid(proc, 1);
   Process_setParent(proc, 1);
   Process_setThreadGroup(proc, 0);

   Process_updateComm(proc, "commof16char");
   Process_updateCmdline(proc, "<unsupported architecture>", 0, 0);
   Process_updateExe(proc, "/path/to/executable");

   const Settings* settings = super->super.host->settings;
   if (settings->ss->flags & PROCESS_FLAG_CWD) {
      free_and_xStrdup(&proc->procCwd, "/current/working/directory");
   }

   proc->super.updated = true;

   proc->state = RUNNING;
   proc->isKernelThread = false;
   proc->isUserlandThread = false;
   proc->super.show = true; /* Reflected in settings-> "hideXXX" really */
   proc->pgrp = 0;
   proc->session = 0;
   proc->tty_nr = 0;
   proc->tty_name = NULL;
   proc->tpgid = 0;
   proc->processor = 0;

   proc->percent_cpu = 2.5;
   proc->percent_mem = 2.5;
   Process_updateCPUFieldWidths(proc->percent_cpu);

   proc->st_uid = 0;
   proc->user = "nobody"; /* Update whenever proc->st_uid is changed */

   proc->priority = 0;
   proc->nice = 0;
   proc->nlwp = 1;
   proc->starttime_ctime = 1433116800; // Jun 01, 2015
   Process_fillStarttimeBuffer(proc);

   proc->m_virt = 100;
   proc->m_resident = 100;

   proc->minflt = 20;
   proc->majflt = 20;

   if (!preExisting)
      ProcessTable_add(super, proc);
}

function: ProcessTable_new

ProcessTable* ProcessTable_new(Machine* host, Hashtable* pidMatchList) {
   UnsupportedProcessTable* this = xCalloc(1, sizeof(UnsupportedProcessTable));
   Object_setClass(this, Class(ProcessTable));

   ProcessTable* super = &this->super;
   ProcessTable_init(super, Class(Process), host, pidMatchList);

   return super;
}

UnsupportedProcessTable.h

unsupported/UnsupportedProcessTable.h

struct: UnsupportedProcessTable_

typedef struct UnsupportedProcessTable_ {
   ProcessTable super;
} UnsupportedProcessTable;

UptimeMeter.c

UptimeMeter.c

function: UptimeMeter_updateValues

static void UptimeMeter_updateValues(Meter* this) {
   int totalseconds = Platform_getUptime();
   if (totalseconds <= 0) {
      xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "(unknown)");
      return;
   }
   int seconds = totalseconds % 60;
   int minutes = (totalseconds / 60) % 60;
   int hours = (totalseconds / 3600) % 24;
   int days = (totalseconds / 86400);

   char daysbuf[32];
   if (days > 100) {
      xSnprintf(daysbuf, sizeof(daysbuf), "%d days(!), ", days);
   } else if (days > 1) {
      xSnprintf(daysbuf, sizeof(daysbuf), "%d days, ", days);
   } else if (days == 1) {
      xSnprintf(daysbuf, sizeof(daysbuf), "1 day, ");
   } else {
      daysbuf[0] = '\0';
   }
   xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
}

global variable: UptimeMeter_attributes

static const int UptimeMeter_attributes[] = {
   UPTIME
};

global variable: UptimeMeter_class

const MeterClass UptimeMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete
   },
   .updateValues = UptimeMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = (1 << TEXT_METERMODE) | (1 << LED_METERMODE),
   .maxItems = 0,
   .total = 0.0,
   .attributes = UptimeMeter_attributes,
   .name = "Uptime",
   .uiName = "Uptime",
   .caption = "Uptime: "
};

UptimeMeter.h

UptimeMeter.h

variable: UptimeMeter_class

extern const MeterClass UptimeMeter_class;

UsersTable.c

UsersTable.c

function: UsersTable_delete

void UsersTable_delete(UsersTable* this) {
   Hashtable_delete(this->users);
   free(this);
}

function: UsersTable_foreach

inline void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData) {
   Hashtable_foreach(this->users, f, userData);
}

function: UsersTable_getRef

char* UsersTable_getRef(UsersTable* this, unsigned int uid) {
   char* name = Hashtable_get(this->users, uid);
   if (name == NULL) {
      const struct passwd* userData = getpwuid(uid);
      if (userData != NULL) {
         name = xStrdup(userData->pw_name);
         Hashtable_put(this->users, uid, name);
      }
   }
   return name;
}

function: UsersTable_new

UsersTable* UsersTable_new(void) {
   UsersTable* this;
   this = xMalloc(sizeof(UsersTable));
   this->users = Hashtable_new(10, true);
   return this;
}

UsersTable.h

UsersTable.h

function: UsersTable_delete

void UsersTable_delete(UsersTable* this);

function: UsersTable_foreach

void UsersTable_foreach(UsersTable* this, Hashtable_PairFunction f, void* userData);

function: UsersTable_getRef

char* UsersTable_getRef(UsersTable* this, unsigned int uid);

function: UsersTable_new

UsersTable* UsersTable_new(void);

struct: UsersTable

typedef struct UsersTable_ {
   Hashtable* users;
} UsersTable;

Vector.c

Vector.c

function: insertionSort

static void insertionSort(Object** array, int left, int right, Object_Compare compare) {
   for (int i = left + 1; i <= right; i++) {
      Object* t = array[i];
      int j = i - 1;
      while (j >= left) {
         //comparisons++;
         if (compare(array[j], t) <= 0)
            break;

         array[j + 1] = array[j];
         j--;
      }
      array[j + 1] = t;
   }
}

function: partition

static int partition(Object** array, int left, int right, int pivotIndex, Object_Compare compare) {
   const Object* pivotValue = array[pivotIndex];
   swap(array, pivotIndex, right);
   int storeIndex = left;
   for (int i = left; i < right; i++) {
      //comparisons++;
      if (compare(array[i], pivotValue) <= 0) {
         swap(array, i, storeIndex);
         storeIndex++;
      }
   }
   swap(array, storeIndex, right);
   return storeIndex;
}

function: quickSort

static void quickSort(Object** array, int left, int right, Object_Compare compare) {
   if (left >= right)
      return;

   int pivotIndex = (left + right) / 2;
   int pivotNewIndex = partition(array, left, right, pivotIndex, compare);
   quickSort(array, left, pivotNewIndex - 1, compare);
   quickSort(array, pivotNewIndex + 1, right, compare);
}

function: swap

static void swap(Object** array, int indexA, int indexB) {
   assert(indexA >= 0);
   assert(indexB >= 0);
   Object* tmp = array[indexA];
   array[indexA] = array[indexB];
   array[indexB] = tmp;
}

function: Vector_add

void Vector_add(Vector* this, void* data_) {
   Object* data = data_;
   assert(Object_isA(data, this->type));
   assert(Vector_isConsistent(this));
   int i = this->items;
   Vector_set(this, this->items, data);
   assert(this->items == i + 1); (void)(i);
   assert(Vector_isConsistent(this));
}

function: Vector_compact

void Vector_compact(Vector* this) {
   if (!Vector_isDirty(this)) {
      return;
   }

   const int size = this->items;
   assert(0 <= this->dirty_index && this->dirty_index < size);
   assert(this->array[this->dirty_index] == NULL);

   int idx = this->dirty_index;

   // one deletion: use memmove, which should be faster
   if (this->dirty_count == 1) {
      memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx - 1) * sizeof(this->array[0]));
      this->array[this->items - 1] = NULL;
   } else {
      // multiple deletions
      for (int i = idx + 1; i < size; i++) {
         if (this->array[i]) {
            this->array[idx++] = this->array[i];
         }
      }
      // idx is now at the end of the vector and on the first index which should be set to NULL
      memset(&this->array[idx], '\0', (size - idx) * sizeof(this->array[0]));
   }

   this->items -= this->dirty_count;
   this->dirty_index = -1;
   this->dirty_count = 0;

   assert(Vector_isConsistent(this));
}

function: Vector_countEquals

bool Vector_countEquals(const Vector* this, unsigned int expectedCount) {
   unsigned int n = 0;
   for (int i = 0; i < this->items; i++) {
      if (this->array[i]) {
         n++;
      }
   }
   return n == expectedCount;
}

function: Vector_delete

void Vector_delete(Vector* this) {
   if (this->owner) {
      for (int i = 0; i < this->items; i++) {
         if (this->array[i]) {
            Object_delete(this->array[i]);
         }
      }
   }
   free(this->array);
   free(this);
}

function: Vector_get

Object* Vector_get(const Vector* this, int idx) {
   assert(idx >= 0 && idx < this->items);
   assert(this->array[idx]);
   assert(Object_isA(this->array[idx], this->type));
   return this->array[idx];
}

function: Vector_indexOf

int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compare) {
   const Object* search = search_;
   assert(Object_isA(search, this->type));
   assert(compare);
   assert(Vector_isConsistent(this));
   for (int i = 0; i < this->items; i++) {
      const Object* o = this->array[i];
      assert(o);
      if (compare(search, o) == 0) {
         return i;
      }
   }
   return -1;
}

function: Vector_insert

void Vector_insert(Vector* this, int idx, void* data_) {
   Object* data = data_;
   assert(idx >= 0);
   assert(Object_isA(data, this->type));
   assert(Vector_isConsistent(this));

   if (idx > this->items) {
      idx = this->items;
   }

   Vector_resizeIfNecessary(this, this->items + 1);
   //assert(this->array[this->items] == NULL);
   if (idx < this->items) {
      memmove(&this->array[idx + 1], &this->array[idx], (this->items - idx) * sizeof(this->array[0]));
   }
   this->array[idx] = data;
   this->items++;
   assert(Vector_isConsistent(this));
}

function: Vector_insertionSort

void Vector_insertionSort(Vector* this) {
   assert(this->type->compare);
   assert(Vector_isConsistent(this));
   insertionSort(this->array, 0, this->items - 1, this->type->compare);
   assert(Vector_isConsistent(this));
}

function: Vector_isConsistent

static bool Vector_isConsistent(const Vector* this) {
   assert(this->items <= this->arraySize);
   assert(!Vector_isDirty(this));

   return true;
}

function: Vector_isDirty

static inline bool Vector_isDirty(const Vector* this) {
   if (this->dirty_count > 0) {
      assert(0 <= this->dirty_index && this->dirty_index < this->items);
      assert(this->dirty_count <= this->items);
      return true;
   }
   assert(this->dirty_index == -1);
   return false;
}

function: Vector_moveDown

void Vector_moveDown(Vector* this, int idx) {
   assert(idx >= 0 && idx < this->items);
   assert(Vector_isConsistent(this));

   if (idx == this->items - 1)
      return;

   Object* temp = this->array[idx];
   this->array[idx] = this->array[idx + 1];
   this->array[idx + 1] = temp;
}

function: Vector_moveUp

void Vector_moveUp(Vector* this, int idx) {
   assert(idx >= 0 && idx < this->items);
   assert(Vector_isConsistent(this));

   if (idx == 0)
      return;

   Object* temp = this->array[idx];
   this->array[idx] = this->array[idx - 1];
   this->array[idx - 1] = temp;
}

function: Vector_new

Vector* Vector_new(const ObjectClass* type, bool owner, int size) {
   Vector* this;

   if (size == DEFAULT_SIZE) {
      size = 10;
   }

   assert(size > 0);
   this = xMalloc(sizeof(Vector));
   *this = (Vector) {
      .growthRate = size,
      .array = xCalloc(size, sizeof(Object*)),
      .arraySize = size,
      .items = 0,
      .type = type,
      .owner = owner,
      .dirty_index = -1,
      .dirty_count = 0,
   };
   return this;
}

function: Vector_prune

void Vector_prune(Vector* this) {
   assert(Vector_isConsistent(this));
   if (this->owner) {
      for (int i = 0; i < this->items; i++) {
         if (this->array[i]) {
            Object_delete(this->array[i]);
         }
      }
   }
   this->items = 0;
   this->dirty_index = -1;
   this->dirty_count = 0;
   memset(this->array, '\0', this->arraySize * sizeof(Object*));
}

function: Vector_quickSortCustomCompare

void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare) {
   assert(compare);
   assert(Vector_isConsistent(this));
   quickSort(this->array, 0, this->items - 1, compare);
   assert(Vector_isConsistent(this));
}

function: Vector_remove

Object* Vector_remove(Vector* this, int idx) {
   Object* removed = Vector_take(this, idx);
   if (this->owner) {
      Object_delete(removed);
      return NULL;
   } else {
      return removed;
   }
}

function: Vector_resizeIfNecessary

static void Vector_resizeIfNecessary(Vector* this, int newSize) {
   assert(newSize >= 0);
   if (newSize > this->arraySize) {
      assert(Vector_isConsistent(this));
      int oldSize = this->arraySize;
      this->arraySize = newSize + this->growthRate;
      this->array = (Object**)xReallocArrayZero(this->array, oldSize, this->arraySize, sizeof(Object*));
   }
   assert(Vector_isConsistent(this));
}

function: Vector_set

void Vector_set(Vector* this, int idx, void* data_) {
   Object* data = data_;
   assert(idx >= 0);
   assert(Object_isA(data, this->type));
   assert(Vector_isConsistent(this));

   Vector_resizeIfNecessary(this, idx + 1);
   if (idx >= this->items) {
      this->items = idx + 1;
   } else {
      if (this->owner) {
         Object* removed = this->array[idx];
         if (removed != NULL) {
            Object_delete(removed);
         }
      }
   }
   this->array[idx] = data;
   assert(Vector_isConsistent(this));
}

function: Vector_size

int Vector_size(const Vector* this) {
   assert(Vector_isConsistent(this));
   return this->items;
}

function: Vector_softRemove

Object* Vector_softRemove(Vector* this, int idx) {
   assert(idx >= 0 && idx < this->items);

   Object* removed = this->array[idx];
   assert(removed);
   if (removed) {
      this->array[idx] = NULL;

      this->dirty_count++;
      if (this->dirty_index < 0 || idx < this->dirty_index) {
         this->dirty_index = idx;
      }

      if (this->owner) {
         Object_delete(removed);
         return NULL;
      }
   }

   return removed;
}

function: Vector_splice

void Vector_splice(Vector* this, Vector* from) {
   assert(Vector_isConsistent(this));
   assert(Vector_isConsistent(from));
   assert(!this->owner);

   int olditems = this->items;
   Vector_resizeIfNecessary(this, this->items + from->items);
   this->items += from->items;
   for (int j = 0; j < from->items; j++) {
      this->array[olditems + j] = from->array[j];
   }
}

function: Vector_take

Object* Vector_take(Vector* this, int idx) {
   assert(idx >= 0 && idx < this->items);
   assert(Vector_isConsistent(this));
   Object* removed = this->array[idx];
   assert(removed);
   this->items--;
   if (idx < this->items) {
      memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx) * sizeof(this->array[0]));
   }
   this->array[this->items] = NULL;
   assert(Vector_isConsistent(this));
   return removed;
}

Vector.h

Vector.h

conditional compilation block: Conditional Debug/Release Implementations

#ifndef NDEBUG

Object* Vector_get(const Vector* this, int idx);
int Vector_size(const Vector* this);

/* Vector_countEquals returns true if the number of non-NULL items
   in the Vector is equal to expectedCount. This is only for debugging
   and consistency checks. */
bool Vector_countEquals(const Vector* this, unsigned int expectedCount);

#else /* NDEBUG */

static inline Object* Vector_get(const Vector* this, int idx) {
   return this->array[idx];
}

static inline int Vector_size(const Vector* this) {
   return this->items;
}

#endif /* NDEBUG */

function: Vector_add

void Vector_add(Vector* this, void* data_);

function: Vector_compact

void Vector_compact(Vector* this);

function: Vector_delete

void Vector_delete(Vector* this);

function: Vector_indexOf

int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compare);

function: Vector_insert

void Vector_insert(Vector* this, int idx, void* data_);

function: Vector_insertionSort

void Vector_insertionSort(Vector* this);

function: Vector_moveDown

void Vector_moveDown(Vector* this, int idx);

function: Vector_moveUp

void Vector_moveUp(Vector* this, int idx);

function: Vector_new

Vector* Vector_new(const ObjectClass* type, bool owner, int size);

function: Vector_prune

void Vector_prune(Vector* this);

function: Vector_quickSort

static inline void Vector_quickSort(Vector* this) {
   Vector_quickSortCustomCompare(this, this->type->compare);
}

function: Vector_quickSortCustomCompare

void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare);

function: Vector_remove

Object* Vector_remove(Vector* this, int idx);

function: Vector_set

void Vector_set(Vector* this, int idx, void* data_);

function: Vector_softRemove

Object* Vector_softRemove(Vector* this, int idx);

function: Vector_splice

void Vector_splice(Vector* this, Vector* from);

function: Vector_take

Object* Vector_take(Vector* this, int idx);

function: Vector_type

static inline const ObjectClass* Vector_type(const Vector* this) {
   return this->type;
}

macro: DEFAULT_SIZE

#ifndef DEFAULT_SIZE
#define DEFAULT_SIZE (-1)
#endif

macro guard: HEADER_Vector

#ifndef HEADER_Vector
#define HEADER_Vector

struct: Vector

typedef struct Vector_ {
   Object** array;
   const ObjectClass* type;
   int arraySize;
   int growthRate;
   int items;
   /* lowest index of a pending soft remove/delete operation,
      used to speed up compaction */
   int dirty_index;
   /* count of soft deletes, required for Vector_count to work in debug mode */
   int dirty_count;
   bool owner;
} Vector;

XUtils.c

XUtils.c

function: compareRealNumbers

/* Compares floating point values for ordering data entries. In this function,
   NaN is considered "less than" any other floating point value (regardless of
   sign), and two NaNs are considered "equal" regardless of payload. */
int compareRealNumbers(double a, double b) {
   int result = isgreater(a, b) - isgreater(b, a);
   if (result)
      return result;
   return !isNaN(a) - !isNaN(b);
}

function: countDigits

/* Counts the number of digits needed to print "n" with a given base.
   If "n" is zero, returns 1. This function expects small numbers to
   appear often, hence it uses a O(log(n)) time algorithm. */
size_t countDigits(size_t n, size_t base) {
   assert(base > 1);
   size_t res = 1;
   for (size_t limit = base; n >= limit; limit *= base) {
      res++;
      if (base && limit > SIZE_MAX / base) {
         break;
      }
   }
   return res;
}

function: countTrailingZeros

#if !defined(HAVE_BUILTIN_CTZ)
// map a bit value mod 37 to its position
static const uint8_t mod37BitPosition[] = {
  32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
  7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
  20, 8, 19, 18
};

/* Returns the number of trailing zero bits */
unsigned int countTrailingZeros(unsigned int x) {
   return mod37BitPosition[(-x & x) % 37];
}
#endif

function: fail

void fail(void) {
   CRT_done();
   abort();

   _exit(1); // Should never reach here
}

function: free_and_xStrdup

void free_and_xStrdup(char** ptr, const char* str) {
   if (*ptr && String_eq(*ptr, str))
      return;

   free(*ptr);
   *ptr = xStrdup(str);
}

function: full_write

ssize_t full_write(int fd, const void* buf, size_t count) {
   ssize_t written = 0;

   while (count > 0) {
      ssize_t r = write(fd, buf, count);
      if (r < 0) {
         if (errno == EINTR)
            continue;

         return r;
      }

      if (r == 0)
         break;

      written += r;
      buf = (const unsigned char*)buf + r;
      count -= (size_t)r;
   }

   return written;
}

function: readfd_internal

ATTR_ACCESS3_W(2, 3)
static ssize_t readfd_internal(int fd, void* buffer, size_t count) {
   if (!count) {
      close(fd);
      return -EINVAL;
   }

   ssize_t alreadyRead = 0;
   count--; // reserve one for null-terminator

   for (;;) {
      ssize_t res = read(fd, buffer, count);
      if (res == -1) {
         if (errno == EINTR)
            continue;

         close(fd);
         *((char*)buffer) = '\0';
         return -errno;
      }

      if (res > 0) {
         assert((size_t)res <= count);

         buffer = ((char*)buffer) + res;
         count -= (size_t)res;
         alreadyRead += res;
      }

      if (count == 0 || res == 0) {
         close(fd);
         *((char*)buffer) = '\0';
         return alreadyRead;
      }
   }
}

function: String_cat

char* String_cat(const char* s1, const char* s2) {
   const size_t l1 = strlen(s1);
   const size_t l2 = strlen(s2);
   if (SIZE_MAX - l1 <= l2) {
      fail();
   }
   char* out = xMalloc(l1 + l2 + 1);
   memcpy(out, s1, l1);
   memcpy(out + l1, s2, l2);
   out[l1 + l2] = '\0';
   return out;
}

function: String_contains_i

inline bool String_contains_i(const char* s1, const char* s2, bool multi) {
   // we have a multi-string search term, handle as special case for performance reasons
   if (multi && strstr(s2, "|")) {
      size_t nNeedles;
      char** needles = String_split(s2, '|', &nNeedles);
      for (size_t i = 0; i < nNeedles; i++) {
         if (strcasestr(s1, needles[i]) != NULL) {
            String_freeArray(needles);
            return true;
         }
      }
      String_freeArray(needles);
      return false;
   } else {
      return strcasestr(s1, s2) != NULL;
   }
}

function: String_freeArray

void String_freeArray(char** s) {
   if (!s) {
      return;
   }
   for (size_t i = 0; s[i] != NULL; i++) {
      free(s[i]);
   }
   free(s);
}

function: String_readLine

char* String_readLine(FILE* fp) {
   const size_t step = 1024;
   size_t bufSize = step;
   char* buffer = xMalloc(step + 1);
   char* at = buffer;
   for (;;) {
      const char* ok = fgets(at, step + 1, fp);
      if (!ok) {
         free(buffer);
         return NULL;
      }
      char* newLine = strrchr(at, '\n');
      if (newLine) {
         *newLine = '\0';
         return buffer;
      } else {
         if (feof(fp)) {
            return buffer;
         }
      }
      bufSize += step;
      buffer = xRealloc(buffer, bufSize + 1);
      at = buffer + bufSize - step;
   }
}

function: String_safeStrncpy

size_t String_safeStrncpy(char* restrict dest, const char* restrict src, size_t size) {
   assert(size > 0);

   size_t i = 0;
   for (; i < size - 1 && src[i]; i++)
      dest[i] = src[i];

   dest[i] = '\0';

   return i;
}

function: String_split

char** String_split(const char* s, char sep, size_t* n) {
   const size_t rate = 10;
   char** out = xCalloc(rate, sizeof(char*));
   size_t ctr = 0;
   size_t blocks = rate;
   const char* where;
   while ((where = strchr(s, sep)) != NULL) {
      size_t size = (size_t)(where - s);
      out[ctr] = xStrndup(s, size);
      ctr++;
      if (ctr == blocks) {
         blocks += rate;
         out = (char**) xRealloc(out, sizeof(char*) * blocks);
      }
      s += size + 1;
   }
   if (s[0] != '\0') {
      out[ctr] = xStrdup(s);
      ctr++;
   }
   out = xRealloc(out, sizeof(char*) * (ctr + 1));
   out[ctr] = NULL;

   if (n)
      *n = ctr;

   return out;
}

function: String_trim

char* String_trim(const char* in) {
   while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
      in++;
   }

   size_t len = strlen(in);
   while (len > 0 && (in[len - 1] == ' ' || in[len - 1] == '\t' || in[len - 1] == '\n')) {
      len--;
   }

   return xStrndup(in, len);
}

function: sumPositiveValues

/* Computes the sum of all positive floating point values in an array.
   NaN values in the array are skipped. The returned sum will always be
   nonnegative. */
double sumPositiveValues(const double* array, size_t count) {
   double sum = 0.0;
   for (size_t i = 0; i < count; i++) {
      if (isPositive(array[i]))
         sum += array[i];
   }
   return sum;
}

function: xAsprintf

int xAsprintf(char** strp, const char* fmt, ...) {
   va_list vl;
   va_start(vl, fmt);
   int r = vasprintf(strp, fmt, vl);
   va_end(vl);

   if (r < 0 || !*strp) {
      fail();
   }

   return r;
}

function: xCalloc

void* xCalloc(size_t nmemb, size_t size) {
   assert(nmemb > 0);
   assert(size > 0);
   if (SIZE_MAX / nmemb < size) {
      fail();
   }
   void* data = calloc(nmemb, size);
   if (!data) {
      fail();
   }
   return data;
}

function: xMalloc

void* xMalloc(size_t size) {
   assert(size > 0);
   void* data = malloc(size);
   if (!data) {
      fail();
   }
   return data;
}

function: xMallocArray

void* xMallocArray(size_t nmemb, size_t size) {
   assert(nmemb > 0);
   assert(size > 0);
   if (SIZE_MAX / nmemb < size) {
      fail();
   }
   return xMalloc(nmemb * size);
}

function: xReadfile

ssize_t xReadfile(const char* pathname, void* buffer, size_t count) {
   int fd = open(pathname, O_RDONLY);
   if (fd < 0)
      return -errno;

   return readfd_internal(fd, buffer, count);
}

function: xReadfileat

ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count) {
   int fd = Compat_openat(dirfd, pathname, O_RDONLY);
   if (fd < 0)
      return -errno;

   return readfd_internal(fd, buffer, count);
}

function: xRealloc

void* xRealloc(void* ptr, size_t size) {
   assert(size > 0);
   void* data = realloc(ptr, size);
   if (!data) {
      /* free'ing ptr here causes an indirect memory leak if pointers
       * are held as part of an potential array referenced in ptr.
       * In GCC 14 -fanalyzer recognizes this leak, but fails to
       * ignore it given that this path ends in a noreturn function.
       * Thus to avoid this confusing diagnostic we opt to leave
       * that pointer alone instead.
       */
      // free(ptr);
      fail();
   }
   return data;
}

function: xReallocArray

void* xReallocArray(void* ptr, size_t nmemb, size_t size) {
   assert(nmemb > 0);
   assert(size > 0);
   if (SIZE_MAX / nmemb < size) {
      fail();
   }
   return xRealloc(ptr, nmemb * size);
}

function: xReallocArrayZero

void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size) {
   assert((ptr == NULL) == (prevmemb == 0));

   if (prevmemb == newmemb) {
      return ptr;
   }

   void* ret = xReallocArray(ptr, newmemb, size);

   if (newmemb > prevmemb) {
      memset((unsigned char*)ret + prevmemb * size, '\0', (newmemb - prevmemb) * size);
   }

   return ret;
}

function: xSnprintf

int xSnprintf(char* buf, size_t len, const char* fmt, ...) {
   assert(len > 0);

   va_list vl;
   va_start(vl, fmt);
   int n = vsnprintf(buf, len, fmt, vl);
   va_end(vl);

   if (n < 0 || (size_t)n >= len) {
      fail();
   }

   return n;
}

function: xStrdup

char* xStrdup(const char* str) {
   char* data = strdup(str);
   if (!data) {
      fail();
   }
   return data;
}

function: xStrndup

char* xStrndup(const char* str, size_t len) {
   char* data = strndup(str, len);
   if (!data) {
      fail();
   }
   return data;
}

XUtils.h

XUtils.h

function: compareRealNumbers

int compareRealNumbers(double a, double b);

function: countDigits

size_t countDigits(size_t n, size_t base);

function: countTrailingZeros

#if defined(HAVE_BUILTIN_CTZ)
static inline unsigned int countTrailingZeros(unsigned int x) {
   return !x ? 32 : __builtin_ctz(x);
}
#else
unsigned int countTrailingZeros(unsigned int x);
#endif

function: fail

ATTR_NORETURN
void fail(void);

function: free_and_xStrdup

ATTR_NONNULL
void free_and_xStrdup(char** ptr, const char* str);

function: full_write

ATTR_NONNULL ATTR_ACCESS3_R(2, 3)
ssize_t full_write(int fd, const void* buf, size_t count);

function: full_write_str

ATTR_NONNULL
static inline ssize_t full_write_str(int fd, const char* str) {
   return full_write(fd, str, strlen(str));
}

function: skipEndOfLine

static inline bool skipEndOfLine(FILE* fp) {
   char buffer[1024];
   while (fgets(buffer, sizeof(buffer), fp)) {
      if (strchr(buffer, '\n')) {
         return true;
      }
   }
   return false;
}

function: String_cat

ATTR_NONNULL ATTR_RETNONNULL ATTR_MALLOC
char* String_cat(const char* s1, const char* s2);

function: String_contains_i

bool String_contains_i(const char* s1, const char* s2, bool multi);

function: String_eq

ATTR_NONNULL
static inline bool String_eq(const char* s1, const char* s2) {
   return strcmp(s1, s2) == 0;
}

function: String_eq_nullable

static inline bool String_eq_nullable(const char* s1, const char* s2) {
   if (s1 == s2)
      return true;

   if (s1 && s2)
      return String_eq(s1, s2);

   return false;
}

function: String_freeArray

void String_freeArray(char** s);

function: String_readLine

ATTR_NONNULL ATTR_MALLOC
char* String_readLine(FILE* fp);

function: String_safeStrncpy

ATTR_NONNULL ATTR_ACCESS3_W(1, 3) ATTR_ACCESS3_R(2, 3)
size_t String_safeStrncpy(char* restrict dest, const char* restrict src, size_t size);

function: String_split

ATTR_NONNULL_N(1) ATTR_RETNONNULL
char** String_split(const char* s, char sep, size_t* n);

function: String_startsWith

ATTR_NONNULL
static inline bool String_startsWith(const char* s, const char* match) {
   return strncmp(s, match, strlen(match)) == 0;
}

function: String_strchrnul

ATTR_NONNULL ATTR_RETNONNULL
static inline char* String_strchrnul(const char* s, int c) {
#ifdef HAVE_STRCHRNUL
   return strchrnul(s, c);
#else
   char* result = strchr(s, c);
   if (result)
      return result;
   return strchr(s, '\0');
#endif
}

function: String_trim

ATTR_NONNULL ATTR_RETNONNULL ATTR_MALLOC
char* String_trim(const char* in);

function: sumPositiveValues

ATTR_NONNULL ATTR_ACCESS3_R(1, 2)
double sumPositiveValues(const double* array, size_t count);

function: xAsprintf

ATTR_FORMAT(printf, 2, 3) ATTR_NONNULL_N(1, 2)
int xAsprintf(char** strp, const char* fmt, ...);

function: xCalloc

ATTR_RETNONNULL ATTR_MALLOC ATTR_ALLOC_SIZE2(1, 2)
void* xCalloc(size_t nmemb, size_t size);

function: xDirfd

static inline int xDirfd(DIR* dirp) {
   int r = dirfd(dirp);
   assert(r >= 0);
   return r;
}

function: xMalloc

ATTR_RETNONNULL ATTR_MALLOC ATTR_ALLOC_SIZE1(1)
void* xMalloc(size_t size);

function: xMallocArray

ATTR_RETNONNULL ATTR_MALLOC ATTR_ALLOC_SIZE2(1, 2)
void* xMallocArray(size_t nmemb, size_t size);

function: xReadfile

ATTR_NONNULL ATTR_ACCESS3_W(2, 3)
ssize_t xReadfile(const char* pathname, void* buffer, size_t count);

function: xReadfileat

ATTR_NONNULL ATTR_ACCESS3_W(3, 4)
ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count);

function: xRealloc

ATTR_RETNONNULL ATTR_ALLOC_SIZE1(2)
void* xRealloc(void* ptr, size_t size);

function: xReallocArray

ATTR_RETNONNULL ATTR_ALLOC_SIZE2(2, 3)
void* xReallocArray(void* ptr, size_t nmemb, size_t size);

function: xReallocArrayZero

ATTR_RETNONNULL ATTR_ALLOC_SIZE2(3, 4)
void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size);

function: xSnprintf

ATTR_FORMAT(printf, 3, 4) ATTR_NONNULL_N(1, 3) ATTR_ACCESS3_W(1, 2)
int xSnprintf(char* buf, size_t len, const char* fmt, ...);

function: xStrdup

ATTR_NONNULL ATTR_RETNONNULL ATTR_MALLOC
char* xStrdup(const char* str);

function: xStrndup

ATTR_NONNULL ATTR_RETNONNULL ATTR_MALLOC ATTR_ACCESS3_R(1, 2)
char* xStrndup(const char* str, size_t len);

global_variable: unitPrefixes

static const char unitPrefixes[] = { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' };

ZfsArcMeter.c

zfs/ZfsArcMeter.c

function: ZfsArcMeter_readStats

void ZfsArcMeter_readStats(Meter* this, const ZfsArcStats* stats) {
   this->total = stats->max;
   this->values[0] = stats->MFU;
   this->values[1] = stats->MRU;
   this->values[2] = stats->anon;
   this->values[3] = stats->header;
   this->values[4] = stats->other;

   // "Hide" the last value so it can
   // only be accessed by index and is not
   // displayed by the Bar or Graph style
   this->curItems = 5;
   this->values[5] = stats->size;
}

method: ZfsArcMeter_display

static void ZfsArcMeter_display(const Object* cast, RichString* out) {
   const Meter* this = (const Meter*)cast;

   if (this->values[5] > 0) {
      char buffer[50];
      Meter_humanUnit(buffer, this->total, sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
      Meter_humanUnit(buffer, this->values[5], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Used:");
      RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
      Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " MFU:");
      RichString_appendAscii(out, CRT_colors[ZFS_MFU], buffer);
      Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " MRU:");
      RichString_appendAscii(out, CRT_colors[ZFS_MRU], buffer);
      Meter_humanUnit(buffer, this->values[2], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Anon:");
      RichString_appendAscii(out, CRT_colors[ZFS_ANON], buffer);
      Meter_humanUnit(buffer, this->values[3], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Hdr:");
      RichString_appendAscii(out, CRT_colors[ZFS_HEADER], buffer);
      Meter_humanUnit(buffer, this->values[4], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Oth:");
      RichString_appendAscii(out, CRT_colors[ZFS_OTHER], buffer);
   } else {
      RichString_writeAscii(out, CRT_colors[METER_TEXT], " ");
      RichString_appendAscii(out, CRT_colors[FAILED_READ], "Unavailable");
   }
}

method: ZfsArcMeter_updateValues

static void ZfsArcMeter_updateValues(Meter* this) {
   char* buffer = this->txtBuffer;
   size_t size = sizeof(this->txtBuffer);
   int written;
   Platform_setZfsArcValues(this);

   written = Meter_humanUnit(buffer, this->values[5], size);
   METER_BUFFER_CHECK(buffer, size, written);

   METER_BUFFER_APPEND_CHR(buffer, size, '/');

   Meter_humanUnit(buffer, this->total, size);
}

struct: ZfsArcMeter_attributes

static const int ZfsArcMeter_attributes[] = {
   ZFS_MFU, ZFS_MRU, ZFS_ANON, ZFS_HEADER, ZFS_OTHER
};

struct: ZfsArcMeter_class

const MeterClass ZfsArcMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = ZfsArcMeter_display,
   },
   .updateValues = ZfsArcMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 6,
   .total = 100.0,
   .attributes = ZfsArcMeter_attributes,
   .name = "ZFSARC",
   .uiName = "ZFS ARC",
   .caption = "ARC: "
};

ZfsArcMeter.h

zfs/ZfsArcMeter.h

function: ZfsArcMeter_readStats

void ZfsArcMeter_readStats(Meter* this, const ZfsArcStats* stats);

variable: ZfsArcMeter_class

extern const MeterClass ZfsArcMeter_class;

ZfsArcStats.h

zfs/ZfsArcStats.h

struct: ZfsArcStats

typedef struct ZfsArcStats_ {
   int enabled;
   int isCompressed;
   unsigned long long int min;
   unsigned long long int max;
   unsigned long long int size;
   unsigned long long int MFU;
   unsigned long long int MRU;
   unsigned long long int anon;
   unsigned long long int header;
   unsigned long long int other;
   unsigned long long int compressed;
   unsigned long long int uncompressed;
} ZfsArcStats;

ZfsCompressedArcMeter.c

zfs/ZfsCompressedArcMeter.c

function: ZfsCompressedArcMeter_display

static void ZfsCompressedArcMeter_display(const Object* cast, RichString* out) {
   const Meter* this = (const Meter*)cast;

   if (this->values[0] > 0) {
      char buffer[50];
      int len;

      Meter_humanUnit(buffer, this->total, sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Uncompressed, ");
      Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
      RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer);
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Compressed, ");
      len = ZfsCompressedArcMeter_printRatioString(this, buffer, sizeof(buffer));
      RichString_appendnAscii(out, CRT_colors[ZFS_RATIO], buffer, len);
      RichString_appendAscii(out, CRT_colors[METER_TEXT], " Ratio");
   } else {
      RichString_writeAscii(out, CRT_colors[METER_TEXT], " ");
      RichString_appendAscii(out, CRT_colors[FAILED_READ], "Compression Unavailable");
   }
}

function: ZfsCompressedArcMeter_printRatioString

static int ZfsCompressedArcMeter_printRatioString(const Meter* this, char* buffer, size_t size) {
   if (this->values[0] > 0) {
      return xSnprintf(buffer, size, "%.2f:1", this->total / this->values[0]);
   }

   return xSnprintf(buffer, size, "N/A");
}

function: ZfsCompressedArcMeter_readStats

void ZfsCompressedArcMeter_readStats(Meter* this, const ZfsArcStats* stats) {
   if ( stats->isCompressed ) {
      this->total = stats->uncompressed;
      this->values[0] = stats->compressed;
   } else {
      // For uncompressed ARC, report 1:1 ratio
      this->total = stats->size;
      this->values[0] = stats->size;
   }
}

function: ZfsCompressedArcMeter_updateValues

static void ZfsCompressedArcMeter_updateValues(Meter* this) {
   Platform_setZfsCompressedArcValues(this);

   ZfsCompressedArcMeter_printRatioString(this, this->txtBuffer, sizeof(this->txtBuffer));
}

global variable: ZfsCompressedArcMeter_attributes

static const int ZfsCompressedArcMeter_attributes[] = {
   ZFS_COMPRESSED
};

struct: ZfsCompressedArcMeter_class

const MeterClass ZfsCompressedArcMeter_class = {
   .super = {
      .extends = Class(Meter),
      .delete = Meter_delete,
      .display = ZfsCompressedArcMeter_display,
   },
   .updateValues = ZfsCompressedArcMeter_updateValues,
   .defaultMode = TEXT_METERMODE,
   .supportedModes = METERMODE_DEFAULT_SUPPORTED,
   .maxItems = 1,
   .total = 100.0,
   .attributes = ZfsCompressedArcMeter_attributes,
   .name = "ZFSCARC",
   .uiName = "ZFS CARC",
   .description = "ZFS CARC: Compressed ARC statistics",
   .caption = "ARC: "
};

ZfsCompressedArcMeter.h

zfs/ZfsCompressedArcMeter.h

function: ZfsCompressedArcMeter_readStats

void ZfsCompressedArcMeter_readStats(Meter* this, const ZfsArcStats* stats);

global variable declaration: ZfsCompressedArcMeter_class

extern const MeterClass ZfsCompressedArcMeter_class;