/*
 * Copyright 2011 Steven Gribble
 *
 *  This file is the solution to an exercise problem posed during
 *  one of the UW CSE 333 lectures (333exercises).
 *
 *  333exercises 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  333exercises 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 333exercises.  If not, see <http://www.gnu.org/licenses/>.
 */

// Lecture 2 Extra Exercise #2:
//
// Write a function that:
//  - accepts a string as a parameter
//  - returns:
//     - the first whitespace-separated word in the string, as
//       a newly allocated string
//     - and, the size of that word

#include <stdio.h>     // needed for the declaration of NULL, printf
#include <stdlib.h>    // needed for malloc, free, EXIT_SUCCESS
#include <assert.h>    // needed for the declaration of assert()
#include <string.h>    // for strlen

// We could use isspace() from the C standard library,
// but we haven't seen that yet, so we'll write our
// own is_space.
int is_space(char c) {
  if ((c == ' ') ||   // space
      (c == '\f') ||  // form feed
      (c == '\n') ||  // newline
      (c == '\r') ||  // carriage return
      (c == '\t') ||  // horizontal tab
      (c == '\v')) {  // vertical tab
    return 1;
  }
  return 0;
}

// FirstWord scans through "string" looking for the
// first whitespace separated word in the string.
//
// If it finds one, it:
//    - malloc's space for that word
//    - copies the word into the malloc'ed space, including a NULL-term
//    - returns a pointer to the word through the "retword" return parameter
//    - returns the length of the returned word (>=0)
//
// If "string" is empty or NULL, returns -1 and doesn't malloc anything.
//
// If malloc fails, returns -2 to indicate out of memory.
int FirstWord(char *string, char **retword) {
  int len, i, retlen;

  // test the degenerate case
  if ((string == NULL) || (string[0] == '\0')) {
    return -1;
  }

  len = strlen(string);
  retlen = len;  // by default, copy the whole string
  // look for whitespace
  for (i = 0; i < len; i++) {
    if (is_space(string[i])) {
      retlen = i;
      break;
    }
  }

  // retlen tells us how many characters to copy from
  // string; we need that plus one extra byte for the
  // null terminator
  *retword = (char *) malloc(sizeof(char) * (retlen + 1));
  if (*retword == NULL) {
    return -2;
  }
  for (i = 0; i < retlen; i++) {
    (*retword)[i] = string[i];
  }
  (*retword)[retlen] = '\0';
  return retlen;
}

// Here's some code to test our function.
int main(int argc, char **argv) {
  char *string1 = "Hello world!";
  char *string2 = "Hello!";
  char *string3 = "";
  char *string4 = NULL;
  char *retstr;
  int retval;

  retval = FirstWord(string1, &retstr);
  assert(retval == 5);
  printf("Should be 'Hello':  '%s'\n", retstr);
  free(retstr);

  retval = FirstWord(string2, &retstr);
  assert(retval == 6);
  printf("Should be 'Hello!':  '%s'\n", retstr);
  free(retstr);

  retval = FirstWord(string3, &retstr);
  assert(retval == -1);

  retval = FirstWord(string4, &retstr);
  assert(retval == -1);

  return EXIT_SUCCESS;
}