LCOV - code coverage report
Current view: top level - libs/libptytest - pty_session.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 89.6 % 96 86
Test Date: 2026-05-07 15:53:08 Functions: 84.6 % 13 11

            Line data    Source code
       1              : /**
       2              :  * @file pty_session.c
       3              :  * @brief PTY session lifecycle, input, and screen inspection.
       4              :  */
       5              : 
       6              : #define _DEFAULT_SOURCE
       7              : #define _XOPEN_SOURCE 600
       8              : 
       9              : #include "pty_internal.h"
      10              : #include <errno.h>
      11              : #include <fcntl.h>
      12              : #include <signal.h>
      13              : #include <stdio.h>
      14              : #include <stdlib.h>
      15              : #include <string.h>
      16              : #include <unistd.h>
      17              : #include <sys/ioctl.h>
      18              : #include <sys/wait.h>
      19              : 
      20              : #if defined(__APPLE__)
      21              : #include <util.h>
      22              : #else
      23              : #include <pty.h>
      24              : #endif
      25              : 
      26              : /* ── Lifecycle ───────────────────────────────────────────────────────── */
      27              : 
      28        21058 : PtySession *pty_open(int cols, int rows) {
      29        21058 :     PtySession *s = calloc(1, sizeof(*s));
      30        21058 :     if (!s) return NULL;
      31        21058 :     s->master_fd = -1;
      32        21058 :     s->child_pid = -1;
      33        21058 :     s->cols = cols;
      34        21058 :     s->rows = rows;
      35        21058 :     s->screen = pty_screen_new(cols, rows);
      36        21058 :     if (!s->screen) { free(s); return NULL; }
      37        21058 :     return s;
      38              : }
      39              : 
      40        21058 : int pty_run(PtySession *s, const char *argv[]) {
      41        21058 :     struct winsize ws = {
      42        21058 :         .ws_row = (unsigned short)s->rows,
      43        21058 :         .ws_col = (unsigned short)s->cols
      44              :     };
      45              : 
      46              :     int master_fd;
      47        21058 :     pid_t pid = forkpty(&master_fd, NULL, NULL, &ws);
      48        21058 :     if (pid < 0) return -1;
      49              : 
      50        21058 :     if (pid == 0) {
      51              :         /* Child process */
      52          222 :         setenv("TERM", "xterm-256color", 1);
      53          222 :         setenv("LC_ALL", "en_US.UTF-8", 1);
      54              :         /* Remove COLUMNS/LINES so the program queries the PTY */
      55          222 :         unsetenv("COLUMNS");
      56          222 :         unsetenv("LINES");
      57          222 :         execvp(argv[0], (char *const *)argv);
      58          222 :         _exit(127);
      59              :     }
      60              : 
      61              :     /* Parent */
      62        20836 :     s->master_fd = master_fd;
      63        20836 :     s->child_pid = pid;
      64              : 
      65              :     /* Set master fd to non-blocking for poll-based reads */
      66        20836 :     int flags = fcntl(master_fd, F_GETFL);
      67        20836 :     if (flags >= 0) fcntl(master_fd, F_SETFL, flags | O_NONBLOCK);
      68              : 
      69        20836 :     return 0;
      70              : }
      71              : 
      72        20836 : void pty_close(PtySession *s) {
      73        20836 :     if (!s) return;
      74              : 
      75        20836 :     if (s->child_pid > 0) {
      76        20836 :         kill(s->child_pid, SIGTERM);
      77              :         /* Give the child 100ms to exit */
      78        20836 :         usleep(100000);
      79              :         int status;
      80        20836 :         if (waitpid(s->child_pid, &status, WNOHANG) == 0) {
      81            0 :             kill(s->child_pid, SIGKILL);
      82            0 :             waitpid(s->child_pid, &status, 0);
      83              :         }
      84              :     }
      85              : 
      86        20836 :     if (s->master_fd >= 0) close(s->master_fd);
      87        20836 :     pty_screen_free(s->screen);
      88        20836 :     free(s);
      89              : }
      90              : 
      91              : /* ── Input ───────────────────────────────────────────────────────────── */
      92              : 
      93       101079 : void pty_send(PtySession *s, const char *bytes, size_t len) {
      94       101079 :     if (!s || s->master_fd < 0) return;
      95       101079 :     ssize_t n = write(s->master_fd, bytes, len);
      96              :     (void)n;
      97              : }
      98              : 
      99        92028 : void pty_send_key(PtySession *s, PtyKey key) {
     100        92028 :     switch (key) {
     101          727 :     case PTY_KEY_UP:     pty_send(s, "\033[A", 3); break;
     102        44095 :     case PTY_KEY_DOWN:   pty_send(s, "\033[B", 3); break;
     103          143 :     case PTY_KEY_RIGHT:  pty_send(s, "\033[C", 3); break;
     104          143 :     case PTY_KEY_LEFT:   pty_send(s, "\033[D", 3); break;
     105          490 :     case PTY_KEY_PGUP:   pty_send(s, "\033[5~", 4); break;
     106          653 :     case PTY_KEY_PGDN:   pty_send(s, "\033[6~", 4); break;
     107         7835 :     case PTY_KEY_HOME:   pty_send(s, "\033[H", 3); break;
     108          443 :     case PTY_KEY_END:    pty_send(s, "\033[F", 3); break;
     109        37499 :     default: {
     110        37499 :         char c = (char)key;
     111        37499 :         pty_send(s, &c, 1);
     112              :     }
     113              :     }
     114        92028 : }
     115              : 
     116         9051 : void pty_send_str(PtySession *s, const char *str) {
     117         9051 :     if (str) pty_send(s, str, strlen(str));
     118         9051 : }
     119              : 
     120              : /* ── Screen inspection ───────────────────────────────────────────────── */
     121              : 
     122            0 : const char *pty_cell_text(PtySession *s, int row, int col) {
     123            0 :     if (!s || !s->screen) return "";
     124            0 :     if (row < 0 || row >= s->rows || col < 0 || col >= s->cols) return "";
     125            0 :     return s->screen->cells[row * s->cols + col].ch;
     126              : }
     127              : 
     128          499 : int pty_cell_attr(PtySession *s, int row, int col) {
     129          499 :     if (!s || !s->screen) return 0;
     130          499 :     if (row < 0 || row >= s->rows || col < 0 || col >= s->cols) return 0;
     131          499 :     return s->screen->cells[row * s->cols + col].attr;
     132              : }
     133              : 
     134          158 : int pty_cell_fg(PtySession *s, int row, int col) {
     135          158 :     if (!s || !s->screen) return PTY_FG_DEFAULT;
     136          158 :     if (row < 0 || row >= s->rows || col < 0 || col >= s->cols) return PTY_FG_DEFAULT;
     137          158 :     return s->screen->cells[row * s->cols + col].fg;
     138              : }
     139              : 
     140      3311163 : char *pty_row_text(PtySession *s, int row, char *buf, size_t size) {
     141      3311163 :     if (!s || !s->screen || !buf || size == 0) { if (buf) buf[0] = '\0'; return buf; }
     142      3311163 :     if (row < 0 || row >= s->rows) { buf[0] = '\0'; return buf; }
     143              : 
     144      3311163 :     size_t pos = 0;
     145    337891323 :     for (int c = 0; c < s->cols && pos + 4 < size; c++) {
     146    334580160 :         const char *ch = s->screen->cells[row * s->cols + c].ch;
     147    334580160 :         size_t len = strlen(ch);
     148    334580160 :         if (pos + len < size) {
     149    334580160 :             memcpy(buf + pos, ch, len);
     150    334580160 :             pos += len;
     151              :         }
     152              :     }
     153      3311163 :     buf[pos] = '\0';
     154              : 
     155              :     /* Trim trailing spaces */
     156    266343272 :     while (pos > 0 && buf[pos - 1] == ' ') buf[--pos] = '\0';
     157              : 
     158      3311163 :     return buf;
     159              : }
     160              : 
     161      3311163 : int pty_row_contains(PtySession *s, int row, const char *text) {
     162              :     char buf[4096];
     163      3311163 :     pty_row_text(s, row, buf, sizeof(buf));
     164      3311163 :     return strstr(buf, text) != NULL;
     165              : }
     166              : 
     167       200269 : int pty_screen_contains(PtySession *s, const char *text) {
     168       200269 :     if (!s || !s->screen) return 0;
     169      3408139 :     for (int r = 0; r < s->rows; r++) {
     170      3291168 :         if (pty_row_contains(s, r, text)) return 1;
     171              :     }
     172       116971 :     return 0;
     173              : }
     174              : 
     175            0 : void pty_get_size(PtySession *s, int *cols, int *rows) {
     176            0 :     if (cols) *cols = s ? s->cols : 0;
     177            0 :     if (rows) *rows = s ? s->rows : 0;
     178            0 : }
        

Generated by: LCOV version 2.0-1