/* 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 . */ #include "getc.h" #include "safe_malloc.h" #include "vars.h" #include #include #include #include #include #include #include #include #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; }