/**
 * Copyright (C) 2023-2026 Linaro Limited (or its affiliates). All rights reserved.
 * Copyright (C) 2012-2023 Arm Limited (or its affiliates).
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */

#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <termios.h>
#include "terminal.h"
#include "terminalutil.h"

Terminal::Terminal(QObject *parent) :
    QLocalSocket(parent),
    mManagerFd(-1),
    mPtsName(0),
    mCurrentProcess(0),
    mColumns(80),
    mRows(12)
{
}

Terminal::~Terminal()
{
    if (mManagerFd >= 0)
        ::close(mManagerFd);
    if (mPtsName)
        ::free(const_cast <char *>(mPtsName));
}

void Terminal::winch()
{
    struct winsize winsize;
    memset(&winsize, 0, sizeof(winsize));
    winsize.ws_row = mRows;
    winsize.ws_col = mColumns;
    winsize.ws_xpixel = mRows * 10;
    winsize.ws_ypixel = mColumns * 10;
    ioctl(mManagerFd, TIOCSWINSZ, &winsize);
}

bool Terminal::openManager()
{
    Q_ASSERT(mManagerFd < 0);
    mManagerFd = posix_openpt(O_RDWR | O_NOCTTY);
    if (mManagerFd < 0)
        return false;
    winch();
    if (grantpt(mManagerFd) < 0) {
        ::close(mManagerFd);
        mManagerFd = -1;
        return false;
    }
    if (unlockpt(mManagerFd) < 0) {
        ::close(mManagerFd);
        mManagerFd = -1;
        return false;
    }
    int flags = fcntl(mManagerFd, F_GETFD, 0);
    if (flags != -1) {
        int newflags = flags | FD_CLOEXEC;
        if (flags != newflags)
            fcntl (mManagerFd, F_SETFD, newflags);
    }
    const char* pseudoterminalName = ptsname(mManagerFd);
    if (pseudoterminalName) {
        mPtsName = strdup(pseudoterminalName);
        setSocketDescriptor(mManagerFd);
        return true;
    } else {
        return false;
    }
}

int Terminal::openSubordinate()
{
    Q_ASSERT(mManagerFd >= 0);
    int subordinateFd = ::open(mPtsName, O_RDWR);
    if (subordinateFd < 0)
        return -1;
    return subordinateFd;
}
