/*
    yamm, Yet Another Micro Monitor
    get_tty.c
    Copyright (C) 1992  Andrea Marangoni
    Copyright (C) 1994, 1995  Riccardo Facchetti

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdio.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#if !defined(linux) || (KERNEL_VERSION < 1001076)
#include <string.h>
#else
#include <linux/string.h>
#endif
#include <sys/sysmacros.h>

#if defined(linux)
#include <linux/major.h>
#endif /* linux */

#include "yamm.h"

/*
 * This code is in HP-UX scandir manual page
 */

typedef struct _mtty {
	int error;
	char name [ MAXNAMLEN + 1 ];
	struct stat stty;
} MTTY;

#define SMTTY    (sizeof(MTTY))
#define BUF      32

static MTTY *list_tty = NULL, *list_pty = NULL;
static char *unknown = "Unknown";
static int n_tty = 0, n_pty = 0;

static int chk4tty(const struct dirent *);

void setup_tty(void)
{
	struct dirent **namelist, **list;
	static char buffer [ MAXNAMLEN + 15 ];
	register MTTY *next;
	register int counter;

/*
**
**                           TTY
**
*/

/*
 * The devices contained in /dev are not all ttys. There are disks,
 * tapes, memory and a lot of other devices.
 * The idea is to search for only the tty devices and not for ALL
 * the devices, just to save memory.
 */
#if defined(linux)
/*
 * For now i use the selection with chk4tty only under linux
 * because I need to know HP-UX tty/pty major numbers.
 * This allow a lot of memory saving.
 */

	if ( ( n_tty = scandir("/dev", &namelist, chk4tty, NULL)) <= 0) 
#else
	if ( ( n_tty = scandir("/dev", &namelist, NULL, NULL)) <= 0) 
#endif /* linux */
		return;

	if ( ( list_tty = ( MTTY *)malloc ( n_tty* SMTTY )) == NULL )
		return;

	for ( next = list_tty, counter = 0, list = namelist;
		counter < n_tty && next;      ++counter, ++next, list++ ) {

		strcpy ( buffer,"/dev/" );
		strcat ( buffer, (*list)->d_name );
		buffer[MAXNAMLEN + 15] = '\0';

		if ( stat ( buffer, &(next->stty) ) == -1 )
			next->error = 1;
		else
			next->error = 0;

		strncpy ( next->name, (*list)->d_name,  MAXNAMLEN );
		next->name[MAXNAMLEN] = '\0';
		free((void *)*list);
	}

#if defined(__hpux)
/*
 * HP-UX have its pty in /dev/pty/ while linux have the pty in /dev/
 * so this code is not needed under linux.
 *
 * - Riccardo
 */

/*
**
**                           PTY
**
*/
	if ( ( n_pty = scandir("/dev/pty", &namelist, NULL, NULL)) <= 0) 
		return;

	if ( ( list_pty = ( MTTY *)malloc ( n_pty* SMTTY )) == NULL )
		return;

	for ( next = list_pty, counter = 0, list = namelist;
		counter < n_pty && next;      ++counter, ++next, list++ ) {

		strcpy ( buffer,"/dev/pty/" );
		strcat ( buffer, (*list)->d_name );
		buffer[MAXNAMLEN + 15] = '\0';

		if ( stat ( buffer, &(next->stty) ) == -1 )
			next->error = 1;
		else
			next->error = 0;

		strncpy ( next->name, (*list)->d_name,  MAXNAMLEN );
		next->name[MAXNAMLEN] = '\0';
		free((void *)*list);
	}
#endif /* __hpux */
}

/* 
 * In stat.h: #define S_ISCHR(_M)  ((_M & _S_IFMT)==_S_IFCHR)
 * Test for char special.
 */
static char *info_tty ( MTTY *pointer )
{
	static char buf [ BUF ];

	if ( !pointer || pointer->error)
		return ( unknown );

	/* BUF << MAXNAMLEN */
	strncpy ( buf, pointer->name, BUF - 14 );
	buf[BUF - 14] = '\0';

	sprintf ( buf + strlen ( buf ), " (%c%c%c%c%c%c%c%c%c)",
		pointer->stty.st_mode & S_IRUSR ? 'r' : '-',
		pointer->stty.st_mode & S_IWUSR ? 'w' : '-',
		pointer->stty.st_mode & S_IXUSR ? 'x' : '-',
		pointer->stty.st_mode & S_IRGRP ? 'r' : '-',
		pointer->stty.st_mode & S_IRGRP ? 'w' : '-',
		pointer->stty.st_mode & S_IRGRP ? 'x' : '-',
		pointer->stty.st_mode & S_IROTH ? 'r' : '-',
		pointer->stty.st_mode & S_IROTH ? 'w' : '-',
		pointer->stty.st_mode & S_IROTH ? 'x' : '-' );

	buf[BUF - 1] = '\0';
	return ( buf );
}
	

/*
 * major(x) and minor(x) are macros defined in sys/sysmacros.h
 */
char *get_tty ( long m_major, long m_minor )
{
	register MTTY *next = NULL;
	register int counter;

	if ( m_major == -1 && m_minor == -1 )
		return ( "?" );
	
	for ( counter = 0, next = list_tty; counter < n_tty && next;
			++counter, ++next ) 

		if ( !next->error &&
		S_ISCHR(next->stty.st_mode) && 
		major(next->stty.st_rdev) == m_major &&
		minor(next->stty.st_rdev) == m_minor )
			return ( info_tty ( next ) );


	for ( counter = 0, next = list_pty; counter < n_pty && next;
			++counter, ++next )

		if ( !next->error &&
		S_ISCHR(next->stty.st_mode) && 
		major(next->stty.st_rdev) == m_major &&
		minor(next->stty.st_rdev) == m_minor )
			return ( info_tty ( next ) );

	return ( unknown );
}

#if defined(linux)
/*
 * select function for scandir:
 * return 1 if dd is OK, 0 if is not wanted
 */
static int chk4tty(const struct dirent *dd) {
	struct stat st;

	if ( stat(dd->d_name, &st) == -1)
		return 0;
	
	if ( major(st.st_rdev) == TTY_MAJOR || major(st.st_rdev) == TTYAUX_MAJOR)
		return 1;
	
	return 0;
}
#endif /* linux */
