/* * Copyright (c) 2010 Jiri Svoboda * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Input module * * Reads source code. Currently input can be read from a file (standard * case), from a string literal (when parsing builtin code) or interactively * from the user. */ #include <stdio.h> #include <stdlib.h> #include "mytypes.h" #include "os/os.h" #include "strtab.h" #include "input.h" /** Size of input buffer. XXX This limits the maximum line length. */ #define INPUT_BUFFER_SIZE 256 static errno_t input_init_file(input_t *input, const char *fname); static void input_init_interactive(input_t *input); static void input_init_string(input_t *input, const char *str); /** Create new input object for reading from file. * * @param input Place to store pointer to new input object. * @param fname Name of file to read from. * * @return EOK on success, ENOMEM when allocation fails, * ENOENT when opening file fails. */ errno_t input_new_file(input_t **input, const char *fname) { *input = malloc(sizeof(input_t)); if (*input == NULL) return ENOMEM; return input_init_file(*input, fname); } /** Create new input object for reading from interactive input. * * @param input Place to store pointer to new input object. * @return EOK on success, ENOMEM when allocation fails. */ errno_t input_new_interactive(input_t **input) { *input = malloc(sizeof(input_t)); if (*input == NULL) return ENOMEM; input_init_interactive(*input); return EOK; } /** Create new input object for reading from string. * * @param input Place to store pointer to new input object. * @param str String literal from which to read input. * @return EOK on success, ENOMEM when allocation fails. */ errno_t input_new_string(input_t **input, const char *str) { *input = malloc(sizeof(input_t)); if (*input == NULL) return ENOMEM; input_init_string(*input, str); return EOK; } /** Initialize input object for reading from file. * * @param input Input object. * @param fname Name of file to read from. * * @return EOK on success, ENOENT when opening file fails. */ static errno_t input_init_file(input_t *input, const char *fname) { FILE *f; f = fopen(fname, "rt"); if (f == NULL) return ENOENT; input->buffer = malloc(INPUT_BUFFER_SIZE); if (input->buffer == NULL) { printf("Memory allocation failed.\n"); exit(1); } input->name = os_str_dup(fname); input->str = NULL; input->line_no = 0; input->fin = f; return EOK; } /** Initialize input object for reading from interactive input. * * @param input Input object. */ static void input_init_interactive(input_t *input) { input->buffer = malloc(INPUT_BUFFER_SIZE); if (input->buffer == NULL) { printf("Memory allocation failed.\n"); exit(1); } input->name = "<user-input>"; input->str = NULL; input->line_no = 0; input->fin = NULL; } /** Initialize input object for reading from string. * * @param input Input object. * @param str String literal from which to read input. */ static void input_init_string(input_t *input, const char *str) { input->buffer = malloc(INPUT_BUFFER_SIZE); if (input->buffer == NULL) { printf("Memory allocation failed.\n"); exit(1); } input->name = "<builtin>"; input->str = str; input->line_no = 0; input->fin = NULL; } /** Get next line of input. * * The pointer stored in @a line is owned by @a input and is valid until the * next call to input_get_line(). The caller is not to free it. The returned * line is terminated with '\n' if another line is coming (potentially empty). * An empty line ("") signals end of input. * * @param input Input object. * @param line Place to store pointer to next line. * * @return EOK on success, EIO on failure. */ errno_t input_get_line(input_t *input, char **line) { const char *prompt; const char *sp; char *dp; char *line_p; size_t cnt; if (input->fin != NULL) { /* Reading from file. */ if (fgets(input->buffer, INPUT_BUFFER_SIZE, input->fin) == NULL) input->buffer[0] = '\0'; if (ferror(input->fin)) return EIO; *line = input->buffer; } else if (input->str != NULL) { /* Reading from a string constant. */ /* Copy one line. */ sp = input->str; dp = input->buffer; cnt = 0; while (*sp != '\n' && *sp != '\0' && cnt < INPUT_BUFFER_SIZE - 2) { *dp++ = *sp++; } /* Advance to start of next line. */ if (*sp == '\n') *dp++ = *sp++; *dp = '\0'; input->str = sp; *line = input->buffer; } else { /* Interactive mode */ if (input->line_no == 0) prompt = "sbi> "; else prompt = "... "; fflush(stdout); if (os_input_line(prompt, &line_p) != EOK) return EIO; *line = line_p; } ++input->line_no; return EOK; } /** Get number of the last provided line of input. * * @param input Input object. * @return Line number of the last provided input line (counting * from 1 up). */ int input_get_line_no(input_t *input) { return input->line_no; }