/* * This file is part of the Advance project. * * Copyright (C) 1999, 2000, 2001, 2002, 2003 Andrea Mazzoleni * * 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 2 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "portable.h" #include "readinfo.h" /* Start size of buffer */ #define INFO_BUF_MIN 64 /* Buffer used for storing last token */ static unsigned info_buf_mac; static unsigned info_buf_max; static char* info_buf_map; /* Position in the stream */ static unsigned info_pos; /* Char */ static unsigned info_row; /* Row */ static unsigned info_col; /* Column */ static int (*info_ptr_get)(void*); static void (*info_ptr_unget)(void*, char); void* info_ptr_arg; /* Initialize */ void info_init(int (*get)(void*), void (*unget)(void*, char), void* arg) { info_buf_max = 0; info_buf_map = 0; info_pos = 0; info_row = 0; info_col = 0; info_ptr_get = get; info_ptr_unget = unget; info_ptr_arg = arg; } /* Deinitialize */ void info_done(void) { free(info_buf_map); } /* Get information of file position */ unsigned info_row_get(void) { return info_row; } unsigned info_col_get(void) { return info_col; } unsigned info_pos_get(void) { return info_pos; } /* Resize the buffer */ static void info_buf_resize(unsigned size) { if (!info_buf_max) info_buf_max = INFO_BUF_MIN; else info_buf_max *= 2; if (size > info_buf_max) info_buf_max = size; info_buf_map = realloc(info_buf_map, info_buf_max); assert(info_buf_map); } /* Add a char to the buffer end */ static inline void info_buf_add(char c) { if (info_buf_mac >= info_buf_max) info_buf_resize(info_buf_mac + 1); info_buf_map[info_buf_mac++] = c; } /* Reset the buffer */ static void info_buf_reset() { info_buf_mac = 0; } /* Return last token text */ const char* info_text_get(void) { /* ensure the buffer end with zero */ if (info_buf_mac==0 || info_buf_map[info_buf_mac-1]!=0) info_buf_add(0); return info_buf_map; } /* Read a char from file */ static int info_getc(void) { int c = info_ptr_get(info_ptr_arg); switch (c) { case EOF: break; case '\n': info_col = 0; ++info_row; ++info_pos; break; default: ++info_col; ++info_pos; break; } return c; } /* Unget a char from file */ static void info_ungetc(int c) { --info_pos; --info_col; info_ptr_unget(info_ptr_arg, c); } static enum info_t get_symbol(int c) { while (c!=EOF && !isspace(c) && c!='(' && c!=')' && c!='\"') { info_buf_add(c); c = info_getc(); } /* no reason to unget space or EOF */ if (c!=EOF && !isspace(c)) info_ungetc(c); return info_symbol; } static unsigned hexdigit(char c) { if (isdigit(c)) return c - '0'; return toupper(c) - 'A' + 10; } static enum info_t get_string(void) { int c = info_getc(); while (c!=EOF && c!='\"') { if (c=='\\') { c = info_getc(); switch (c) { case 'a' : info_buf_add('\a'); break; case 'b' : info_buf_add('\b'); break; case 'f' : info_buf_add('\f'); break; case 'n' : info_buf_add('\n'); break; case 'r' : info_buf_add('\r'); break; case 't' : info_buf_add('\t'); break; case 'v' : info_buf_add('\v'); break; case '\\' : info_buf_add('\\'); break; case '?' : info_buf_add('\?'); break; case '\'' : info_buf_add('\''); break; case '\"' : info_buf_add('\"'); break; case 'x' : { int d0, d1; unsigned char cc; d0 = info_getc(); if (!isxdigit(d0)) return info_error; d1 = info_getc(); if (!isxdigit(d1)) return info_error; cc = hexdigit(d0) * 16 + hexdigit(d1); info_buf_add(cc); } break; default: return info_error; } } else { info_buf_add(c); } c = info_getc(); } if (c!='\"') return info_error; return info_string; } /* Extract a token */ enum info_t info_token_get(void) { int c = info_getc(); /* reset the buffer */ info_buf_reset(); /* skip space */ while (c!=EOF && isspace(c)) { c = info_getc(); } /* get token */ switch (c) { case EOF: return info_eof; case '(': return info_open; case ')': return info_close; case '\"': return get_string(); default: return get_symbol(c); } } /* Skip a value token * note: * Skip recusively any info_open and info_close * return: * info_error error * otherwise last token skipped */ enum info_t info_skip_value(void) { /* read value token */ enum info_t t = info_token_get(); switch (t) { case info_open: t = info_token_get(); if (t==info_error) return info_error; while (t!=info_close) { /* first read type as a symbol */ if (t!=info_symbol) return info_error; /* second skip the value */ t = info_skip_value(); /* two value required */ if (t==info_error) return info_error; /* read next token, a type or a info_close */ t = info_token_get(); if (t==info_error) return info_error; } break; case info_symbol: case info_string: break; default: return info_error; } return t; }