Here's a small but complete program that does almost what you are asking for. It's working with arrays of integers instead of cstrings, completely dynamically. It should be fairly easy to change it for cstrings (I've included comments).
Actually it is easier when working with cstrings, since you don't have to NUL terminate the columns. Most probably you won't need to tokenize each input line, but store it as a whole instead, after s_get()'ing it....
Most of the action takes place inside the function arr_new()...
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define MAXINPUT (255+1)
#define STOP_INPUT "stop"
/* -----------------------------------------------
* Get a string from stdin, drop the '\n' and return its length.
*/
int s_get( char *s, int size )
{
int i, c;
if ( !s )
return -1;
for (i=0; i < size-1 && EOF != (c = getchar()) && c != '\n'; i++ )
*s++ = c;
*s = '\0';
return i;
}
/* -----------------------------------------------
* Set the first nrows of arr to NULL.
*/
void arr_invalidate_rows( int **arr, int nrows )
{
int i;
if ( !arr )
return;
for (i=0; i < nrows; i++)
arr[i] = NULL;
}
/* -----------------------------------------------
* Print the contents of arr.
* Rows are expected to be NULL terminated,
* while cols are expected to be INT_MAX terminated.
*/
void arr_print( int **arr, const char *title )
{
int i,j;
if ( title ) {
printf( "%s", title );
fflush( stdout );
}
if ( !arr ) {
puts( "<NULL>" );
return;
}
for (i=0; NULL != arr[i]; i++)
{
for (j=0; INT_MAX != arr[i][j]; j++) {
printf( "%d ", arr[i][j] );
fflush(stdout);
}
putchar( '\n' );
}
putchar( '\n' );
}
/* -----------------------------------------------
* Free memory reserved for arr.
* Rows are expected to be NULL terminated.
*/
void arr_free( int ***arr )
{
int i;
if ( !arr )
return;
for (i=0; (*arr)[i]; i++)
free( (*arr)[i] );
free( *arr );
*arr = NULL;
}
/* -----------------------------------------------
* Return a newly created arr, according to user input.
* nrows passes to the caller the total number of rows
* created, excluding the NULL terminating one.
*/
int **arr_new( int *nrows, const char *prompt )
{
const int AHEAD = 16; /* alloc ahead buffer, in elements */
int **ret = NULL, **try = NULL;
int *tryCols = NULL;
int i=0, j=0;
char *delims = "\t\n ";
int ncols = 0;
char input[MAXINPUT] = {'\0'};
if ( !nrows )
goto fail;
if ( prompt ) {
printf( "%s", prompt );
fflush( stdout );
}
/* create alloc-ahead buffer for rows */
*nrows = AHEAD;
ret = malloc( *nrows * sizeof(int *) );
if ( NULL == ret )
goto fail;
arr_invalidate_rows( ret, *nrows ); /* make them all NULL */
/*
* read input lines (rows) from stdin
* tokenize each line to integers (cols)
* and store each integer into each curent row as a new col
*/
for (i=0; s_get(input, MAXINPUT) > 0; i++ )
{
char *cp = NULL; /* string token */
/* stop when user types the STOP_INPUT string */
if ( 0 == strcmp(input, STOP_INPUT) )
break;
/* need more mem for rows? */
if ( i == *nrows-1 )
{
/* double their allocated size */
try = realloc(ret, 2 * (*nrows) * sizeof(int *) );
if ( NULL == try )
goto fail;
arr_invalidate_rows( &try[i], *nrows );
ret = try;
(*nrows) += (*nrows); /* remember new count of rows */
}
/* create alloc-ahead buffer for cols in current row */
ncols = AHEAD;
ret[i] = calloc(ncols, sizeof(int) );
if ( NULL == ret[i] )
goto fail;
/* tokenize input line into integers & store them as cols at current row */
j = 0;
for (cp = strtok(input,delims); cp; cp = strtok(NULL,delims) )
{
/* need more mem for cols in current row ? */
if ( j == ncols-1 )
{
/* double their allocated size */
tryCols = realloc(ret[i], 2 * ncols * sizeof(int) );
if ( NULL == try )
goto fail;
ret[i] = tryCols;
ncols += ncols; /* remember new count of cols */
}
/* convert string token to integer & store it */
if ( 1 != sscanf(cp, "%d", &ret[i][j]) )
goto fail;
/* INT_MAX is allowed, but no further inputting for this row */
if ( INT_MAX == ret[i][j] )
break;
j++; /* current col */
}
ret[i][j++] = INT_MAX; /* add terminating value */
/* free any extra cols from memory for this row */
tryCols = realloc(ret[i], j * sizeof(int) );
if ( NULL == tryCols )
goto fail;
ret[i] = tryCols;
ncols = j-1; /* exclude terminating value */
}
ret[i++] = NULL; /* NULL termninate rows */
/* free any extra rows from memory */
try = realloc( ret, i * sizeof(int *) );
if ( NULL == try )
goto fail;
ret = try;
*nrows = i-1; /* exclude NULL terminating row */
return ret;
fail:
puts( "*** failure to make a new arr... " );
if ( ret )
arr_free( &ret );
return NULL;
}
/* -----------------------------------------------
* Entry point of the program
*/
int main( void )
{
int **arr1 = NULL, **arr2 = NULL;
int nrows1 = 0, nrows2 = 0;
arr1 = arr_new( &nrows1, "ENTER CONTENTS OF ARR1:\n" );
if ( NULL == arr1 )
goto fail;
arr2 = arr_new( &nrows2, "ENTER CONTENTS OF ARR2:\n" );
if ( NULL == arr2 )
goto fail;
arr_print( arr1, "ARR1 CONTENTS:\n" );
arr_print( arr2, "ARR2 CONTENTS:\n" );
arr_free( &arr1 );
arr_free( &arr2 );
exit(0);
fail:
puts( "aborting program... " );
if ( arr1 )
arr_free( &arr1 );
if ( arr2 )
arr_free( &arr2 );
exit(1);
}
And here is some sample input & output...
Code:
ENTER CONTENTS OF ARR1:
1 3 4
0 2
1 3
0 2 4
0 3
stop
ENTER CONTENTS OF ARR2:
5 5 7
5 7
7 6
5 6 6
7 6
stop
ARR1 CONTENTS:
1 3 4
0 2
1 3
0 2 4
0 3
ARR2 CONTENTS:
5 5 7
5 7
7 6
5 6 6
7 6
s_get() requires a pre-allocated buffer of fixed maximum size. If you want it to be dynamically allocated, you may give a try to this function instead (don't forget to free() the returned cstring after done using it).