/* qnx_ps.c - ps-like tool for QNX 8.0 with -f option for full command line */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <pwd.h>
#include <libgen.h>
#include <stdint.h>
#include <limits.h>

#include <sys/procfs.h>
#include <sys/debug.h>
#include <devctl.h>

#define PROC_DIR "/proc"
#define MAX_CMDLINE 4096

static void print_header(int full)
{
    if (full) {
        printf("%-8s %-7s %-7s %-3s %-8s %-6s %-8s %s\n",
               "UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD");
    } else {
        printf("%-8s %-7s %-7s %-3s %-8s %-6s %-8s %s\n",
               "UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD");
    }
}

int main(int argc, char **argv)
{
    int full_cmdline = 0;

    if (argc > 1 && (strcmp(argv[1], "-f") == 0 || strcmp(argv[1], "-F") == 0)) {
        full_cmdline = 1;
    }

    DIR *dir;
    struct dirent *entry;
    char path[256];
    int fd;

    procfs_info pinfo;
    procfs_status tinfo;

    struct {
        procfs_debuginfo info;
        char             buff[PATH_MAX];
    } mapdbg;

    print_header(full_cmdline);

    dir = opendir(PROC_DIR);
    if (!dir) {
        perror("opendir /proc");
        return 1;
    }

    while ((entry = readdir(dir)) != NULL) {
        pid_t pid = (pid_t)atoi(entry->d_name);
        if (pid <= 0) continue;

        snprintf(path, sizeof(path), "%s/%d/ctl", PROC_DIR, pid);
        fd = open(path, O_RDONLY);
        if (fd == -1) continue;

        memset(&pinfo, 0, sizeof(pinfo));
        if (devctl(fd, DCMD_PROC_INFO, &pinfo, sizeof(pinfo), NULL) != EOK) {
            close(fd);
            continue;
        }

        memset(&tinfo, 0, sizeof(tinfo));
        if (devctl(fd, DCMD_PROC_STATUS, &tinfo, sizeof(tinfo), NULL) != EOK) {
            close(fd);
            continue;
        }

        /* Get executable basename for short mode */
        memset(&mapdbg, 0, sizeof(mapdbg));
        if (devctl(fd, DCMD_PROC_MAPDEBUG_BASE, &mapdbg, sizeof(mapdbg), NULL) != EOK) {
            strcpy(mapdbg.info.path, "<unknown>");
        }

        /* Read full command line if requested */
        char cmdline_buf[MAX_CMDLINE] = {0};
        const char *display_cmd = "<unknown>";

        if (full_cmdline) {
            char cmdpath[256];
            snprintf(cmdpath, sizeof(cmdpath), "%s/%d/cmdline", PROC_DIR, pid);
            int cfd = open(cmdpath, O_RDONLY);
            if (cfd != -1) {
                ssize_t n = read(cfd, cmdline_buf, sizeof(cmdline_buf) - 1);
                close(cfd);
                if (n > 0) {
                    /* Replace null bytes with space for readable output */
                    for (ssize_t i = 0; i < n; i++) {
                        if (cmdline_buf[i] == '\0') cmdline_buf[i] = ' ';
                    }
                    display_cmd = cmdline_buf;
                }
            }
            if (display_cmd == "<unknown>") {
                /* fallback to basename */
                display_cmd = basename(mapdbg.info.path);
            }
        } else {
            /* short mode */
            display_cmd = basename(mapdbg.info.path);
            if (!display_cmd || display_cmd[0] == '\0')
                display_cmd = "<unknown>";
        }

        close(fd);

        uid_t uid  = pinfo.uid;
        pid_t ppid = pinfo.parent;

        uint64_t cpu_ns = tinfo.sutime;
        unsigned long total_sec = (unsigned long)(cpu_ns / 1000000000ULL);
        if (total_sec == 0 && cpu_ns > 0) total_sec = 1;

        int cpu_rough = (total_sec >= 5) ? 1 : 0;

        time_t now = time(NULL);
        time_t start_time = now - (time_t)(total_sec + 5);   /* rough */

        char stime_buf[9] = "??:??:??";
        strftime(stime_buf, sizeof(stime_buf), "%H:%M:%S", localtime(&start_time));

        char time_buf[9];
        snprintf(time_buf, sizeof(time_buf), "%02lu:%02lu:%02lu",
                 total_sec / 3600, (total_sec % 3600) / 60, total_sec % 60);

        const char *tty = "?";

        struct passwd *pw = getpwuid(uid);

        printf("%-8s %-7d %-7d %-3d %-8s %-6s %-8s %s\n",
               pw ? pw->pw_name : "?",
               pid,
               ppid,
               cpu_rough,
               stime_buf,
               tty,
               time_buf,
               display_cmd);
    }

    closedir(dir);
    return 0;
}
