OpenSolaris_b135/lib/krb5/ss/listen.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Listener loop for subsystem library libss.a.
 *
 *	util/ss/listen.c
 * 
 * Copyright 1987, 1988 by MIT Student Information Processing Board
 *
 * For copyright information, see copyright.h.
 */

#include "copyright.h"
#include "ss_internal.h"
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <termios.h>
#include <libintl.h>
#include <sys/param.h>
/* Solaris Kerberos */
#include <libtecla.h>

#define	MAX_LINE_LEN BUFSIZ
#define	MAX_HIST_LEN 8192

static ss_data *current_info;
static jmp_buf listen_jmpb;

static RETSIGTYPE print_prompt()
{
    struct termios termbuf;

    if (tcgetattr(STDIN_FILENO, &termbuf) == 0) {
	termbuf.c_lflag |= ICANON|ISIG|ECHO;
	tcsetattr(STDIN_FILENO, TCSANOW, &termbuf);
    }
    (void) fputs(current_info->prompt, stdout);
    (void) fflush(stdout);
}

static RETSIGTYPE listen_int_handler(signo)
    int signo;
{
    putc('\n', stdout);
    longjmp(listen_jmpb, 1);
}
/* Solaris Kerberos */
typedef struct _ss_commands {
	int sci_idx;
	const char **cmd;
	unsigned int count;
} ss_commands;

/*
 * Solaris Kerberos
 * get_commands fills out a ss_commands structure with pointers
 * to the top-level commands (char*) that a program supports.
 * count reflects the number of commands cmd holds. Memory must
 * be allocated by the caller.
 */
void get_commands(ss_commands *commands) {
	const char * const *cmd;
	ss_request_table **table;
	ss_request_entry *request;
	ss_data *info;

	commands->count = 0;

	info = ss_info(commands->sci_idx);
	for (table = info->rqt_tables; *table; table++) {
		for (request = (*table)->requests;
		    request->command_names != NULL; request++) {
			for (cmd = request->command_names;
			    cmd != NULL && *cmd != NULL; cmd++) {
				if (commands->cmd != NULL)
					commands->cmd[commands->count] = *cmd;
				commands->count++;
			}
		}
	}
}

/*
 * Solaris Kerberos
 * Match function used by libtecla for tab-completion.
 */
CPL_MATCH_FN(cmdmatch) {
	int argc, len, ws, i;
	char **argv, *l;
	ss_commands *commands = data;
	int ret = 0;

	/* Dup the line as ss_parse will modify the string */
	l = strdup(line);
	if (l == NULL)
		return (ret);

	/* Tab-completion may happen in the middle of a line */
	if (word_end != strlen(l))
		l[word_end] = '\0';

	if (ss_parse(commands->sci_idx, l, &argc, &argv, 1)) {
		free (l);
		return (ret);
	}
		
	/* Don't bother if the arg count is not 1 or 0 */
	if (argc < 2) {
		len = argc ? strlen(argv[0]) : 0;
		ws = word_end - len;

		for (i = 0; i < commands->count; i++) {
			if (strncmp(commands->cmd[i], line + ws, len) == 0) {
				ret = cpl_add_completion(cpl, line, ws,
				    word_end, commands->cmd[i] + len, "", " ");
				if (ret)
					break;
			}
		}
	}

	free(argv);
	free(l);
	return (ret);
}

int ss_listen (sci_idx)
    int sci_idx;
{
    register char *cp;
    register ss_data *info;
    char buffer[BUFSIZ];
    char *volatile end = buffer;
    int code;

    /* Solaris Kerberos */
    char *input;
    GetLine *gl;
    GlReturnStatus ret;
    ss_commands commands;

    jmp_buf old_jmpb;
    ss_data *old_info = current_info;
#ifdef POSIX_SIGNALS
    struct sigaction isig, csig, nsig, osig;
    sigset_t nmask, omask;
#else
    register RETSIGTYPE (*sig_cont)();
    RETSIGTYPE (*sig_int)(), (*old_sig_cont)();
    int mask;
#endif
    
    current_info = info = ss_info(sci_idx);
    info->abort = 0;

    /* Solaris Kerberos */
    gl = new_GetLine(MAX_LINE_LEN, MAX_HIST_LEN);
    if (gl == NULL) {
        ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN,
            "new_GetLine() failed.\n"));
    	current_info = old_info;
	return (SS_ET_TECLA_ERR);
    }

    commands.sci_idx = sci_idx;
    commands.cmd = NULL;

    /* Find out how many commands there are */
    get_commands(&commands);

    /* Alloc space for them */
    commands.cmd = malloc(sizeof (char *) * commands.count);
    if (commands.cmd == NULL) {
    	current_info = old_info;
    	gl = del_GetLine(gl);
	return (ENOMEM);
    }

    /* Fill-in commands.cmd */
    get_commands(&commands);

    if (gl_customize_completion(gl, &commands, cmdmatch) != 0 ) {
	ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN,
            "failed to register completion function.\n"));
	free(commands.cmd);
    	current_info = old_info;
    	gl = del_GetLine(gl);
	return (SS_ET_TECLA_ERR);
    }

#ifdef POSIX_SIGNALS
    csig.sa_handler = (RETSIGTYPE (*)())0;
    sigemptyset(&nmask);
    sigaddset(&nmask, SIGINT);
    sigprocmask(SIG_BLOCK, &nmask, &omask);
#else
    sig_cont = (RETSIGTYPE (*)())0;
    mask = sigblock(sigmask(SIGINT));
#endif

    memcpy(old_jmpb, listen_jmpb, sizeof(jmp_buf));

#ifdef POSIX_SIGNALS
    nsig.sa_handler = listen_int_handler;
    sigemptyset(&nsig.sa_mask);
    nsig.sa_flags = 0;
    sigaction(SIGINT, &nsig, &isig);
#else
    sig_int = signal(SIGINT, listen_int_handler);
#endif

    setjmp(listen_jmpb);

#ifdef POSIX_SIGNALS
    sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
#else
    (void) sigsetmask(mask);
#endif

    /*
     * Solaris Kerberos:
     * Let libtecla deal with SIGINT when it's doing its own processing
     * otherwise the input line won't be cleared on SIGINT.
     */
    if (gl_trap_signal(gl, SIGINT, GLS_DONT_FORWARD, GLS_ABORT, NULL)) {
        ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN,
            "Failed to trap SIGINT.\n"));
	code = SS_ET_TECLA_ERR;
	goto egress;
    }

    while(!info->abort) {
	print_prompt();
	*end = '\0';
#ifdef POSIX_SIGNALS
	nsig.sa_handler = listen_int_handler;	/* fgets is not signal-safe */
	osig = csig;
	sigaction(SIGCONT, &nsig, &csig);
	if ((RETSIGTYPE (*)())csig.sa_handler==(RETSIGTYPE (*)())listen_int_handler)
	    csig = osig;
#else
	old_sig_cont = sig_cont;
	sig_cont = signal(SIGCONT, print_prompt);
	if (sig_cont == print_prompt)
	    sig_cont = old_sig_cont;
#endif

        /* Solaris Kerberos */
        input = gl_get_line(gl, info->prompt, NULL, -1);
        ret = gl_return_status(gl);

        switch (ret) {
            case (GLR_SIGNAL):
                gl_abandon_line(gl);
                continue;
            case (GLR_EOF):
                info->abort = 1;
                continue;
            case (GLR_ERROR):
                ss_error(sci_idx, 0, dgettext(TEXT_DOMAIN,
                    "Failed to read line: %s\n"), gl_error_message(gl, NULL, 0));
                info->abort = 1;
		code = SS_ET_TECLA_ERR;
		goto egress;
        }
	cp = strchr(input, '\n');
	if (cp) {
	    *cp = '\0';
	    if (cp == input)
		continue;
	}
#ifdef POSIX_SIGNALS
	sigaction(SIGCONT, &csig, (struct sigaction *)0);
#else
	(void) signal(SIGCONT, sig_cont);
#endif
	for (end = input; *end; end++)
	    ;

	code = ss_execute_line (sci_idx, input);
	if (code == SS_ET_COMMAND_NOT_FOUND) {
	    register char *c = input;
	    while (*c == ' ' || *c == '\t')
		c++;
	    cp = strchr (c, ' ');
	    if (cp)
		*cp = '\0';
	    cp = strchr (c, '\t');
	    if (cp)
		*cp = '\0';
	    ss_error (sci_idx, 0, dgettext(TEXT_DOMAIN,
		    "Unknown request \"%s\".  Type \"?\" for a request list."),
		       c);
	}
    }
    code = 0;
egress:

    /* Solaris Kerberos */
    free(commands.cmd);
    gl = del_GetLine(gl);

#ifdef POSIX_SIGNALS
    sigaction(SIGINT, &isig, (struct sigaction *)0);
#else
    (void) signal(SIGINT, sig_int);
#endif
    memcpy(listen_jmpb, old_jmpb, sizeof(jmp_buf));
    current_info = old_info;
    return code;
}

void ss_abort_subsystem(sci_idx, code)
    int sci_idx;
    int code;
{
    ss_info(sci_idx)->abort = 1;
    ss_info(sci_idx)->exit_status = code;
    
}

void ss_quit(argc, argv, sci_idx, infop)
    int argc;
    char const * const *argv;
    int sci_idx;
    pointer infop;
{
    ss_abort_subsystem(sci_idx, 0);
}