text: /*
   * Copyright (c) 2018 Oticon A/S
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <stdio.h>
  #include <ctype.h>
  #include "init.h"
  #include "kernel.h"
  #include "console/console.h"
  #include "posix_board_if.h"
  #include <string.h>
  #include <sys/time.h>
  #include <sys/select.h>
  #include <unistd.h>
  
  #define DEBUG_ECHO 0
  
  #if (DEBUG_ECHO)
  #define ECHO(...) printf(__VA_ARGS__)
  #else
  #define ECHO(...)
  #endif
  
  #if defined(CONFIG_NATIVE_POSIX_STDOUT_CONSOLE)
  /**
   *
   * @brief Initialize the driver that provides the printk output
   *
   */
  static void native_posix_stdout_init(void)
  {
  	/* Let's ensure that even if we are redirecting to a file, we get stdout
  	 * and stderr line buffered (default for console). Note that glibc
  	 * ignores size. But just in case we set a reasonable number in case
  	 * somebody tries to compile against a different library
  	 */
  	setvbuf(stdout, NULL, _IOLBF, 512);
  	setvbuf(stderr, NULL, _IOLBF, 512);
  
  	extern void __printk_hook_install(int (*fn)(int));
  	__printk_hook_install(putchar);
  }
  
  /**
   * Ensure that whatever was written thru printk is displayed now
   */
  void posix_flush_stdout(void)
  {
  	fflush(stdout);
  }
  #endif /* CONFIG_NATIVE_POSIX_STDOUT_CONSOLE */
  
  #if defined(CONFIG_NATIVE_POSIX_STDIN_CONSOLE)
  
  #define VALID_DIRECTIVES \
  	"Valid native console driver directives:\n" \
  	"  !wait %%u\n" \
  	"  !quit\n"
  
  static struct k_fifo *avail_queue;
  static struct k_fifo *lines_queue;
  static u8_t (*completion_cb)(char *line, u8_t len);
  static bool stdin_is_tty;
  
  static K_THREAD_STACK_DEFINE(stack, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);
  static struct k_thread native_stdio_thread;
  
  static inline void found_eof(void)
  {
  	/*
  	 * Once stdin is closed or the input file has ended,
  	 * there is no need to try again
  	 */
  	ECHO("Got EOF\n");
  	k_thread_abort(&native_stdio_thread);
  }
  
  /*
   * Check if the command is a directive the driver handles on its own
   * and if it is, handle it.
   * If not return 0 (so it can be passed to the shell)
   *
   * Inputs
   *   s       Command string
   *   towait  Pointer to the amount of time wait until attempting to receive
   *           the next command
   *
   * return 0 if it is not a directive
   * return > 0 if it was a directive (command starts with '!')
   * return 2 if the driver directive requires to pause processing input
   */
  static int catch_directive(char *s, s32_t *towait)
  {
  	while (*s != 0  && isspace(*s)) {
  		s++;
  	}
  
  	if (*s != '!') {
  		return 0;
  	}
  
  	if (strncmp(s, "!wait", 5) == 0) {
  		int ret;
  
  		ret = sscanf(&s[5], "%i", towait);
  		if (ret != 1) {
  			posix_print_error_and_exit("%s(): '%s' not understood, "
  						   "!wait syntax: !wait %%i\n",
  						   __func__, s);
  		}
  		return 2;
  	} else if (strcmp(s, "!quit") == 0) {
  		posix_exit(0);
  	}
  
  	posix_print_warning("%s(): '%s' not understood\n" VALID_DIRECTIVES,
  			    __func__, s);
  
  	return 1;
  }
  
  /**
   * Check if there is data ready in stdin
   */
  static int stdin_not_ready(void)
  {
  	int ready;
  	fd_set readfds;
  	struct timeval timeout;
  
  	timeout.tv_usec = 0;
  	timeout.tv_sec  = 0;
  
  	FD_ZERO(&readfds);
  	FD_SET(STDIN_FILENO, &readfds);
  
  	ready = select(STDIN_FILENO+1, &readfds, NULL, NULL, &timeout);
  
  	if (ready == 0) {
  		return 1;
  	} else if (ready == -1) {
  		posix_print_error_and_exit("%s: Error on select ()\n",
  					   __func__);
  	}
  
  	return 0;
  }
  
  /**
   * Check if there is any line in the stdin buffer,
   * if there is and we have available shell buffers feed it to the shell
   *
   * This function returns how long the thread should wait in ms,
   * before checking again the stdin buffer
   */
  static s32_t attempt_read_from_stdin(void)
  {
  	static struct console_input *cmd;
  	s32_t towait = CONFIG_NATIVE_STDIN_POLL_PERIOD;
  
  	while (1) {
  		char *ret;
  		int last;
  		int is_directive;
  
  		if (feof(stdin)) {
  			found_eof();
  		}
  
  		/*
  		 * If stdin comes from a terminal, we check if the user has
  		 * input something, and if not we pause the process.
  		 *
  		 * If stdin is not coming from a terminal, but from a file or
  		 * pipe, we always proceed to try to get data and block until
  		 * we do
  		 */
  		if (stdin_is_tty && stdin_not_ready()) {
  			return towait;
  		}
  
  		/* Pick next available shell line buffer */
  		if (!cmd) {
  			cmd = k_fifo_get(avail_queue, K_NO_WAIT);
  			if (!cmd) {
  				return towait;
  			}
  		}
  
  		/*
  		 * By default stdin is (_IOLBF) line buffered when connected to
  		 * a terminal and fully buffered (_IOFBF) when connected to a
  		 * pipe/file.
  		 * If we got a terminal: we already checked for it to be ready
  		 * and therefore a full line should be there for us.
  		 *
  		 * If we got a pipe or file we will block until we get a line,
  		 * or we reach EOF
  		 */
  		ret = fgets(cmd->line, CONSOLE_MAX_LINE_LEN, stdin);
  		if (ret == NULL) {
  			if (feof(stdin)) {
  				found_eof();
  			}
  			/*
  			 * Otherwise this was an unexpected error we do
  			 * not try to handle
  			 */
  			return towait;
  		}
  
  		/* Remove a possible end of line and other trailing spaces */
  		last = (int)strlen(cmd->line) - 1;
  		while ((last >= 0) && isspace(cmd->line[last])) {
  			cmd->line[last--] = 0;
  		}
  
  		ECHO("Got: \"%s\"\n", cmd->line);
  
  		/*
  		 * This console has a special set of directives which start with
  		 * "!" which we capture here
  		 */
  		is_directive = catch_directive(cmd->line, &towait);
  		if (is_directive == 2) {
  			return towait;
  		} else if (is_directive > 0) {
  			continue;
  		}
  
  		/* Let's give it to the shell to handle */
  		k_fifo_put(lines_queue, cmd);
  		cmd = NULL;
  	}
  
  	return towait;
  }
  
  /**
   * This thread will check if there is any new line in the stdin buffer
   * every CONFIG_NATIVE_STDIN_POLL_PERIOD ms
   *
   * If there is, it will feed it to the shell
   */
  static void native_stdio_runner(void *p1, void *p2, void *p3)
  {
  	stdin_is_tty = isatty(STDIN_FILENO);
  
  	while (1) {
  		s32_t wait_time = attempt_read_from_stdin();
  
  		k_sleep(wait_time);
  	}
  }
  #endif /* CONFIG_NATIVE_POSIX_STDIN_CONSOLE */
  
  static int native_posix_console_init(struct device *arg)
  {
  	ARG_UNUSED(arg);
  
  #if defined(CONFIG_NATIVE_POSIX_STDOUT_CONSOLE)
  	native_posix_stdout_init();
  #endif
  
  	return 0;
  }
  
  SYS_INIT(native_posix_console_init, PRE_KERNEL_1,
  	CONFIG_NATIVE_POSIX_CONSOLE_INIT_PRIORITY);
  