home *** CD-ROM | disk | FTP | other *** search
- /**************************************************************************
- * text.c: Functions for text input/output.
- * Part of MP, the MIDI Playground.
- *
- * Author: Daniel Barrett
- * Version: See the file "version.h".
- * Copyright: None! This program is in the Public Domain.
- * Please share it with others.
- ***************************************************************************/
-
-
- #include "mp.h"
-
-
- /* Given the currently read character and the current state, compute
- * the new state and the MIDI value. */
-
- STATE_T NewState(int c, STATE_T state, MIDI_VALUE *answer)
- {
- static BOOL inString = FALSE;
-
- if (inString)
- return(DoInString(c, state, answer, &inString));
- else if (c == HELP_SYMBOL)
- {
- InputHelp();
- return(state);
- }
- else if (state == STATE_NORMAL && c == '\"')
- {
- inString = TRUE;
- return(STATE_NORMAL); /* Doesn't matter. */
- }
- else
- return(NonStringState(c, state, answer));
- }
-
- /****************************************************************************
- * Our finite automaton for non-strings. Process the character and return
- * the new state.
- ****************************************************************************/
-
- STATE_T NonStringState(int c, STATE_T state, MIDI_VALUE *answer)
- {
- switch (state)
- {
- case STATE_NORMAL: return(DoNormal(c, answer));
- case STATE_INCOMMENT: return(DoComment(c));
- case STATE_INDECIMAL: return(DoDecimal(c, answer));
- case STATE_INOCTAL: return(DoOctal(c, answer));
- case STATE_STARTHEX: return(DoHex(c, answer));
- case STATE_INHEX: return(DoInHex(c, answer));
- case STATE_LEADZERO: return(DoLeadZero(c, answer));
- case STATE_INCHAR: return(DoInChar(c, answer));
- case STATE_STARTBINARY: return(DoBinary(c, answer));
- case STATE_INBINARY: return(DoInBinary(c, answer));
- case STATE_EXPECTQUOTE: return(DoExpectQuote(c, answer));
- case STATE_BACKSLASHINCHAR:
- return(DoBackslash(c, answer, STATE_EXPECTQUOTE));
- case STATE_SUCCESS:
- case STATE_OVERFLOW:
- case STATE_ERROR: return(state);
- }
- }
-
-
- /****************************************************************************
- * We are not currently in a number. A character is read; decide what to
- * do with it.
- ****************************************************************************/
-
- STATE_T DoNormal(int c, MIDI_VALUE *answer)
- {
- if (isspace(c))
- return(STATE_NORMAL);
- else if (c == START_COMMENT)
- return(STATE_INCOMMENT);
- else if (c == START_BINARY)
- return(STATE_STARTBINARY);
- else if (c == '\'')
- return(STATE_INCHAR);
- else if (c == '0')
- return(STATE_LEADZERO);
- else if (toupper(c) == 'H')
- return(STATE_STARTHEX);
- else if (isdigit(c))
- {
- *answer = D_TO_INT(c);
- return(STATE_INDECIMAL);
- }
- else if (isxdigit(c))
- {
- *answer = H_TO_INT(c);
- return(STATE_INHEX);
- }
- else
- return(STATE_ERROR);
- }
-
-
- /****************************************************************************
- * Base 10 (decimal) numbers.
- ****************************************************************************/
-
- STATE_T DoDecimal(int c, MIDI_VALUE *answer)
- {
- if (isdigit(c))
- return(IncreaseIfPossible(answer, D_TO_INT(c), 10,
- STATE_INDECIMAL));
- else if (isspace(c))
- return(STATE_SUCCESS);
- else
- return(STATE_ERROR);
- }
-
-
- /****************************************************************************
- * Octal numbers.
- ****************************************************************************/
-
- STATE_T DoOctal(int c, MIDI_VALUE *answer)
- {
- if (isoctal(c))
- return(IncreaseIfPossible(answer, D_TO_INT(c), 8,
- STATE_INOCTAL));
- else if (isspace(c))
- return(STATE_SUCCESS);
- else
- return(STATE_ERROR);
- }
-
-
- /****************************************************************************
- * Hexadecimal numbers.
- ****************************************************************************/
-
- STATE_T DoHex(int c, MIDI_VALUE *answer)
- {
- if (isxdigit(c))
- {
- *answer = H_TO_INT(c);
- return(STATE_INHEX);
- }
- else
- return(STATE_ERROR);
- }
-
-
- STATE_T DoInHex(int c, MIDI_VALUE *answer)
- {
- if (isxdigit(c))
- return(IncreaseIfPossible(answer, H_TO_INT(c), 16,
- STATE_INHEX));
- else if (isspace(c))
- return(STATE_SUCCESS);
- else
- return(STATE_ERROR);
- }
-
-
- /****************************************************************************
- * Binary numbers.
- ****************************************************************************/
-
- STATE_T DoBinary(int c, MIDI_VALUE *answer)
- {
- if ((c == '0') || (c == '1'))
- {
- *answer = D_TO_INT(c);
- return(STATE_INBINARY);
- }
- else
- return(STATE_ERROR);
- }
-
-
- STATE_T DoInBinary(int c, MIDI_VALUE *answer)
- {
- if ((c == '0') || (c == '1'))
- return(IncreaseIfPossible(answer, D_TO_INT(c), 2,
- STATE_INBINARY));
- else if (isspace(c))
- return(STATE_SUCCESS);
- else
- return(STATE_ERROR);
- }
-
-
- /****************************************************************************
- * Hook for negative numbers. Commented out right now.
- ****************************************************************************/
-
- #ifdef ALLOW_NEGATIVE
- STATE_T DoNegative(int c, MIDI_VALUE *answer)
- {
- if (c == '0')
- return(STATE_LEADZERO);
- else if (isdigit(c))
- {
- return(STATE_INDECIMAL);
- }
- else
- return(STATE_ERROR);
- }
- #endif /* ALLOW_NEGATIVE */
-
-
- /****************************************************************************
- * Leading zero was found: Do octal or hexadecimal as required.
- ****************************************************************************/
-
- STATE_T DoLeadZero(int c, MIDI_VALUE *answer)
- {
- if (toupper(c) == 'X')
- return(STATE_STARTHEX);
- else if (isoctal(c))
- {
- *answer = D_TO_INT(c);
- return(STATE_INOCTAL);
- }
- else if (isspace(c))
- return(STATE_SUCCESS);
- else
- return(STATE_ERROR);
- }
-
-
- /****************************************************************************
- * Append the digit "newNum" onto the right of the number *answer.
- * Don't allow overflow. Works for any base. Return newState on success.
- ****************************************************************************/
-
- STATE_T IncreaseIfPossible(MIDI_VALUE *answer, int newNum, int base,
- STATE_T newState)
- {
- if ((*answer) > (LARGEST_VALUE / base))
- return(STATE_OVERFLOW);
- else
- {
- *answer *= base;
-
- if ((*answer) > (LARGEST_VALUE - newNum))
- return(STATE_OVERFLOW);
- else
- {
- *answer += newNum;
- return(newState);
- }
- }
- }
-
-
- /****************************************************************************
- * Character-oriented routines.
- ****************************************************************************/
-
- STATE_T DoInChar(int c, MIDI_VALUE *answer)
- {
- if (c == '\\')
- return(STATE_BACKSLASHINCHAR);
- else
- {
- *answer = c;
- return(STATE_EXPECTQUOTE);
- }
- }
-
-
- STATE_T DoExpectQuote(int c, MIDI_VALUE *answer)
- {
- return((c == '\'') ? STATE_SUCCESS : STATE_ERROR);
- }
-
-
- STATE_T DoBackslash(int c, MIDI_VALUE *answer, STATE_T newState)
- {
- switch (c)
- {
- case '0': *answer = '\0'; break;
- case 'a': *answer = '\a'; break;
- case 'b': *answer = '\b'; break;
- case 'f': *answer = '\f'; break;
- case 'n': *answer = '\n'; break;
- case 'r': *answer = '\r'; break;
- case 't': *answer = '\t'; break;
- case 'v': *answer = '\v'; break;
- case '\\':
- case '\'':
- case '\"': *answer = c; break;
- default: return(STATE_ERROR);
- }
- return(newState);
- }
-
-
- /****************************************************************************
- * String-oriented routines.
- ****************************************************************************/
-
- STATE_T DoInString(int c, STATE_T state, MIDI_VALUE *answer, BOOL *inString)
- {
- switch (state)
- {
- case STATE_NORMAL:
- if (c == '\"')
- {
- *inString = FALSE;
- return(STATE_NORMAL);
- }
- else if (c == '\\')
- return(STATE_BACKSLASHINSTRING);
- else
- {
- *answer = isspace(c) ? ' ' : c;
- return(STATE_SUCCESS);
- }
- break;
- case STATE_BACKSLASHINSTRING:
- return(DoBackslash(c, answer, STATE_SUCCESS));
- }
- }
-
-
- /****************************************************************************
- * Handling comments. Everything from comment symbol to the end of the
- * line is a comment.
- ****************************************************************************/
-
- STATE_T DoComment(int c)
- {
- return( (c == '\n') ? STATE_NORMAL : STATE_INCOMMENT );
- }
-
- /****************************************************************************
- * Getting help.
- ****************************************************************************/
-
-
- void InputHelp()
- {
- fprintf(stderr, "INPUT\tMEANING\n"
- "1-9...\tDecimal number.\n"
- "0...\tOctal number.\n"
- "0x...\tHexadecimal number (case-insensitive).\n"
- "H...\tHexadecimal number (case-insensitive).\n"
- "A-F...\tHexadecimal number (case-insensitive).\n"
- "#...\tBinary number.\n"
- "'x'\tThe character 'x'."
- " C character syntax like \\n and \\t is valid.\n"
- "\"...\"\tA text string."
- " (Newlines turn into space characters.)\n"
- ";...\tA comment (from semicolon to end of line.\n"
- "Largest legal input value is %ld (base 10).\n"
- "All values must be separated by whitespace.\n\n",
- LARGEST_VALUE);
- }
-
-
- /****************************************************************************
- * Text output functions.
- ****************************************************************************/
-
- void PrintNumber(MIDI_VALUE number, FILE *out)
- {
- fprintf(out, "Dec %3ld, Oct %3lo, Hex %3lX",
- number, number, number);
-
- PrintBinary(number, out);
-
- if (isprint((char)number))
- fprintf(out, ", Char '%c'", number);
-
- putc('\n', out);
- }
-
-
- void PrintBinary(MIDI_VALUE number, FILE *out)
- {
- char buf[BITS_IN_MIDI_VALUE + 1];
- int i, on;
-
- for (i=0; i<BITS_IN_MIDI_VALUE; i++)
- {
- on = number & (1 << i);
- buf[BITS_IN_MIDI_VALUE-i-1] = '0' + (char)(on && 1);
- }
-
- buf[BITS_IN_MIDI_VALUE] = '\0';
- fprintf(out, ", Bin %s", buf);
- }
-