+struct unit_info {
+ const char *id;
+ const char *description;
+ const char *load_state;
+ const char *active_state;
+ const char *sub_state;
+ const char *following;
+ const char *unit_path;
+ uint32_t job_id;
+ const char *job_type;
+ const char *job_path;
+};
+
+static int compare_unit_info(const void *a, const void *b) {
+ const char *d1, *d2;
+ const struct unit_info *u = a, *v = b;
+
+ d1 = strrchr(u->id, '.');
+ d2 = strrchr(v->id, '.');
+
+ if (d1 && d2) {
+ int r;
+
+ if ((r = strcasecmp(d1, d2)) != 0)
+ return r;
+ }
+
+ return strcasecmp(u->id, v->id);
+}
+
+static bool output_show_job(const struct unit_info *u) {
+ const char *dot;
+
+ return (!arg_type || ((dot = strrchr(u->id, '.')) &&
+ streq(dot+1, arg_type))) &&
+ (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
+}
+
+static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
+ unsigned active_len, sub_len, job_len, n_shown = 0;
+ const struct unit_info *u;
+
+ active_len = sizeof("ACTIVE")-1;
+ sub_len = sizeof("SUB")-1;
+ job_len = sizeof("JOB")-1;
+
+ for (u = unit_infos; u < unit_infos + c; u++) {
+ if (!output_show_job(u))
+ continue;
+
+ active_len = MAX(active_len, strlen(u->active_state));
+ sub_len = MAX(sub_len, strlen(u->sub_state));
+ if (u->job_id != 0)
+ job_len = MAX(job_len, strlen(u->job_type));
+ }
+
+ if (on_tty()) {
+ printf("%-25s %-6s %-*s %-*s %-*s", "UNIT", "LOAD",
+ active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
+ if (columns() >= 80+12 || arg_full)
+ printf(" %s\n", "DESCRIPTION");
+ else
+ printf("\n");
+ }
+
+ for (u = unit_infos; u < unit_infos + c; u++) {
+ char *e;
+ int a = 0, b = 0;
+ const char *on_loaded, *off_loaded;
+ const char *on_active, *off_active;
+
+ if (!output_show_job(u))
+ continue;
+
+ n_shown++;
+
+ if (!streq(u->load_state, "loaded") &&
+ !streq(u->load_state, "banned")) {
+ on_loaded = ansi_highlight(true);
+ off_loaded = ansi_highlight(false);
+ } else
+ on_loaded = off_loaded = "";
+
+ if (streq(u->active_state, "failed")) {
+ on_active = ansi_highlight(true);
+ off_active = ansi_highlight(false);
+ } else
+ on_active = off_active = "";
+
+ e = arg_full ? NULL : ellipsize(u->id, 25, 33);
+
+ printf("%-25s %s%-6s%s %s%-*s %-*s%s%n",
+ e ? e : u->id,
+ on_loaded, u->load_state, off_loaded,
+ on_active, active_len, u->active_state,
+ sub_len, u->sub_state, off_active,
+ &a);
+
+ free(e);
+
+ a -= strlen(on_loaded) + strlen(off_loaded);
+ a -= strlen(on_active) + strlen(off_active);
+
+ if (u->job_id != 0)
+ printf(" %-*s", job_len, u->job_type);
+ else
+ b = 1 + job_len;
+
+ if (a + b + 1 < columns()) {
+ if (u->job_id == 0)
+ printf(" %-*s", job_len, "");
+
+ if (arg_full)
+ printf(" %s", u->description);
+ else
+ printf(" %.*s", columns() - a - b - 1, u->description);
+ }
+
+ fputs("\n", stdout);
+ }
+
+ if (on_tty()) {
+ printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n"
+ "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
+ "SUB = The low-level unit activation state, values depend on unit type.\n"
+ "JOB = Pending job for the unit.\n");
+
+ if (arg_all)
+ printf("\n%u units listed.\n", n_shown);
+ else
+ printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
+ }
+}
+