View of xos/usr/xsh/getc.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   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 3 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, see <http://www.gnu.org/licenses/>. */
 
#include "getc.h"
#include "safe_malloc.h"
#include "vars.h"
 
#include <readline/readline.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
 
#define max(a, b) ((a) > (b) ? (a) : (b))
 
static const char *p = NULL;
 
int full_str = 0; /* non nul si on lit une chaine et qu'on l'a lue entierement */
 
/* Lecture dans un flux */
 
/* Comme il est necessaire de savoir vider le tampon du flux d'entree, et que
 * les flux standards n'offrent pas cette fonctionnalite, nous devons redefinir
 * nos propres tampons. */
 
#define DEFAULT_INPUT_STREAM_BUFFER_SIZE 8192
 
int input_stream_fd;
 
static int input_stream_initialized = 0;
static int input_stream_seekable;
static char *input_stream_buffer;
static size_t input_stream_buffer_size;
static int input_stream_buffer_end;
static int input_stream_buffer_pos;
 
static void init_input_stream()
{
  struct stat statbuf;
  size_t size;
 
  input_stream_seekable = lseek(input_stream_fd, 0, SEEK_CUR) != -1;
  if (input_stream_seekable) {
    size = fstat(input_stream_fd, &statbuf) != -1 ? statbuf.st_size : DEFAULT_INPUT_STREAM_BUFFER_SIZE;
    input_stream_buffer = safe_malloc(size);
    input_stream_buffer_size = size;
    input_stream_buffer_pos = input_stream_buffer_end = 0;
  }
  input_stream_initialized = 1;
}
 
int stream_getc()
{
  char c;
 
  /* initialisation paresseuse */
  if (!input_stream_initialized)
    init_input_stream();
 
  if (input_stream_seekable) {
    if (input_stream_buffer_pos == input_stream_buffer_end) {
      if ((input_stream_buffer_end = read(input_stream_fd, input_stream_buffer, input_stream_buffer_size)) == EOF)
        return EOF;
      input_stream_buffer_pos = 0;
      if (!input_stream_buffer_end) /* fin de fichier */
        return EOF;
    }
    return input_stream_buffer[input_stream_buffer_pos++];
  }
  else {
    if (read(input_stream_fd, &c, 1) != 1)
      return EOF;
    return c;
  }
}
 
void sync_input_stream()
{
  if (input_stream_initialized && input_stream_seekable) {
    if (input_stream_buffer_pos < input_stream_buffer_end)
      lseek(input_stream_fd, input_stream_buffer_pos - input_stream_buffer_end, SEEK_CUR);
    input_stream_buffer_pos = input_stream_buffer_end = 0;
  }
}
 
/* Lecture avec readline */
 
#define PROMPT_BUF_GROW_SIZE 64
#define TIMEBUF_SIZE 128
 
static char *readline_line;
 
char *primary_prompt;
char *secondary_prompt;
 
static char *expand_prompt(const char *prompt)
{
  char *buf;
  size_t buf_size;
  int idx;
  int state;
  int num;
  char c;
  char *s, *s1;
  time_t tloc;
  const char *ftime;
  size_t len, newsize;
  int i;
 
  buf = safe_malloc(PROMPT_BUF_GROW_SIZE);
  buf_size = PROMPT_BUF_GROW_SIZE;
  idx = 0;
  state = 0;
  num = 0;
  while ((c = *prompt++)) {
    switch (state) {
    case 0:
      if (c == '\\') {
        state = 1;
        continue;
      }
      goto append_char;
    case 1:
      switch (c) {
      case 'a':
        c = '\a';
        goto append_char;
      case 'd':
        ftime = "%a %b %d";
        goto append_time_string;
      case 'e':
        c = '\e';
        goto append_char;
      case 'n':
        c = '\n';
        goto append_char;
      case 'r':
        c = '\r';
        goto append_char;
      case 's':
        s = safe_strdup(program_invocation_short_name);
        goto append_string;
      case 't':
        ftime = "%H:%M:%S";
        goto append_time_string;
      case 'T':
        ftime = "%I:%M:%S";
        goto append_time_string;
      case '@':
        ftime = "%I:%M %p";
        goto append_time_string;
      case 'A':
        ftime = "%H:%M";
        goto append_time_string;
      case 'w':
        s = safe_malloc(PATH_MAX);
        if (!getcwd(s, PATH_MAX))
          s[0] = '\0';
        goto append_string;
      case 'W':
        s = safe_malloc(PATH_MAX);
        if (getcwd(s, PATH_MAX)) {
          s1 = strrchr(s, '/');
          memmove(s, s1 + 1, s1 - s + 2);
        }
        else
          s[0] = '\0';
        goto append_string;
      case '0' ... '7':
        num = c - '0';
        state = 2;
        continue;
      case '\\':
        c = '\\';
        goto append_char;
      case '[':
        c = '\001';
        goto append_char;
      case ']':
        c = '\002';
        goto append_char;
      default:
        s = safe_malloc(3);
        s[0] = '\\';
        s[1] = c;
        s[2] = '\0';
        goto append_string;
      }
    case 2 ... 3:
      if (c >= '0' && c <= '7') {
        num = num * 8 + (c - '0');
        if (state == 3) {
          c = num;
          goto append_char;
        }
        else {
          state++;
          continue;
        }
      }
      else {
        s = safe_malloc(state + 2);
        s[0] = '\\';
        for (i = 0; i < state - 1; i++) {
          s[state - 1 - i] = '0' + num % 8;
          num /= 8;
        }
        s[state] = c;
        s[state + 1] = '\0';
        goto append_string;
      }
    }
  append_char:
    if ((size_t)(idx + 1) >= buf_size) {
      buf = safe_realloc(buf, buf_size + PROMPT_BUF_GROW_SIZE);
      buf_size += PROMPT_BUF_GROW_SIZE;
    }
    buf[idx++] = c;
    state = 0;
    continue;
  append_time_string:
    s = safe_malloc(TIMEBUF_SIZE);
    time(&tloc);
    if (strftime(s, TIMEBUF_SIZE, ftime, localtime(&tloc)) == 0)
      *s = '\0';
    goto append_string;
  append_string:
    len = strlen(s);
    if (idx + len >= buf_size) {
      newsize = buf_size + max(len, PROMPT_BUF_GROW_SIZE) ;
      buf = safe_realloc(buf, newsize);
      buf_size = newsize;
    }
    strcpy(buf + idx, s);
    idx += len;
    free(s);
    state = 0;
    continue;
  }
  buf[idx] = '\0';
  return buf;
}
 
int readline_getc()
{
  char *expanded_prompt;
  size_t len;
  char c;
 
  if (!readline_line) {
    expanded_prompt = expand_prompt(!history_line ? primary_prompt : secondary_prompt);
    readline_line = readline(expanded_prompt);
    free(expanded_prompt);
    if (!readline_line) {
      if (!history_line) {
        fputc('\n', rl_outstream);
        fflush(rl_outstream);
      }
      return EOF;
    }
    if (!history_line)
      history_line = strdup(readline_line);
    else {
      len = strlen(history_line);
      history_line = safe_realloc(history_line, len + strlen(readline_line) + 2);
      history_line[len++] = '\n';
      strcpy(history_line + len, readline_line);
    }
    p = readline_line;
  }
  if (!(c = *p)) {
    free(readline_line);
    readline_line = NULL;
    return '\n';
  }
  p++;
  return (unsigned char)c;
}
 
/* Lecture dans une chaine */
 
char *command_str;
 
int string_getc()
{
  char c;
 
  if (!p)
    p = command_str;
  if (!(c = *p))
    return EOF;
  p++;
  if (!*p)
    full_str = 1;
  return (unsigned char)c;
}