Sona 0.50 Source

Sona 0.50/tools/mml2sona/text.c

#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "main.h"
#include "text.h"

//***************************************************************************
// read_line
// Reads a single line from a text file. Handles all LF, CR and CRLF line
// endings. Used for reading the MML file.
//---------------------------------------------------------------------------
// return: pointer to string
//---------------------------------------------------------------------------
// note: you must deallocate the string with free() when you're done
//***************************************************************************

char *read_line(FILE *file)
{
   // Allocate initial buffer
   size_t bufpos = 0;
   size_t buflen = 0x80;
   
   char *buffer = (char*) malloc(buflen + 1);
   if (buffer == NULL) {
      fprintf(stderr, ERRORMSG_NOMEMORY);
      exit(EXIT_FAILURE);
   }
   
   // Read until we find a newline
   for (;;) {
      // Read character
      int ch = fgetc(file);
      
      // Nul characters interfere with the parser
      // You normally shouldn't find them in a MML file but just in case...
      if (ch == '\0')
         ch = ' ';
      
      // End of line?
      if (ch == '\n' || ch == EOF) {
         break;
      }
      if (ch == '\r') {
         ch = fgetc(file);
         if (ch != '\n') ungetc(ch, file);
         break;
      }
      
      // Convert all other blank characters into spaces
      // This simplifies parsing the MML
      if (ch == '\t' || ch == '\v' || ch == '\f')
         ch = ' ';
      
      // Do we need to allocate more space in the buffer?
      if (bufpos == buflen) {
         buflen *= 2;
         buffer = (char*) realloc(buffer, buflen + 1);
         if (buffer == NULL) {
            fprintf(stderr, ERRORMSG_NOMEMORY);
            exit(EXIT_FAILURE);
         }
      }
      
      // Insert character into the buffer
      buffer[bufpos] = ch;
      bufpos++;
   }
   
   // Terminate the string and return it
   buffer[bufpos] = '\0';
   return buffer;
}

//***************************************************************************
// skip_spaces
// Returns a pointer to the first character in a string that isn't an ASCII
// space (U+0020). If there are no non-spaces left it returns a pointer to
// the end of the string (U+0000).
//---------------------------------------------------------------------------
// param text: pointer to string
//---------------------------------------------------------------------------
// return: pointer to first non-space character
//***************************************************************************

const char *skip_spaces(const char *text)
{
   while (*text == ' ') text++;
   return text;
}

//***************************************************************************
// skip_nonspaces
// The opposite of skip_spaces, returns a pointer to the first character in a
// string that's an ASCII space (U+0020). If there are no spaces left it
// returns a pointer to the end of the string (U+0000).
//---------------------------------------------------------------------------
// param text: pointer to string
//---------------------------------------------------------------------------
// return: pointer to first space character
//***************************************************************************

const char *skip_nonspaces(const char *text)
{
   while (*text != ' ' && *text != '\0') text++;
   return text;
}

//***************************************************************************
// get_number
// Parses an int-sized integer from a string.
//---------------------------------------------------------------------------
// param text: pointer to text (see notes)
// param result: pointer to variable where to store the result
//---------------------------------------------------------------------------
// return: 0 on success, -1 if no number
//---------------------------------------------------------------------------
// notes: this function takes a pointer to the pointer of the string. On
// success, that pointer is updated to point past the number. On failure,
// the pointer is left intact, and the stored result is 0.
//***************************************************************************

int get_number(const char **text, int *result)
{
   const char *ptr = skip_spaces(*text);
   
   // Where the pointer to the end of the number is stored
   // Annoyingly, strtol() expects char* instead of const char* which means
   // we have to drop the constness. Gotta be careful to make sure that we
   // never attempt to write through it.
   char *endptr;
   
   // Try to read the number
   // Make sure to check for overflow and such
   errno = 0;
   long number = strtol(ptr, &endptr, 10);
   
   if (errno) goto error;
   if (ptr == endptr) goto error;
   if (sizeof(int) < sizeof(long)) {
      if (number < INT_MIN) goto error;
      if (number > INT_MAX) goto error;
   }
   
   // We're done
   *text = endptr;
   *result = number;
   return 0;
   
error:
   // Oops
   *result = 0;
   return -1;
}