#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;
#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;
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)
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;
}
}
#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;
}
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;
}