ftp.nice.ch/pub/next/science/mathematics/workbench/ImprovConnector.s.tar.gz#/ImprovConnector/ImprovConnector.m

This is ImprovConnector.m in view mode; [Download] [Up]

// ImprovConnector.m
// By Judy D. Halchin, Educational Computing Services, Allegheny College.
// Copyright 1993 Allegheny College.  
// You may freely copy, distribute and reuse this code. 
// Allegheny College and the author disclaim any warranty of any kind, 
// expressed or implied, as to its fitness for any particular use.
// This work was partially supported by a Teacher Preparation grant from the 
// National Science Foundation.

#import "ImprovConnector.h"
#import "imxPublicAPI.h"

@implementation ImprovConnector

enum errorMessages
{
	IC_GETSELECTION = NX_APPBASE,
	IC_GETINFO,
	IC_GETCELLVALUE,
	IC_SELECTIONTYPE,
	IC_GETRANGE,
	IC_SETSELECTION,
	IC_ADDITEMS,
	IC_RENAME,
	IC_SETCELLVALUE,
	IC_UPDATE,
	IC_NUMBERCOLUMNS,
	IC_NEWSHEET,
	IC_BADPARAMETERS,
	IC_GETROWS,
	IC_GETCOLUMNS,
	IC_GETCATEGORIES,
	IC_NUMBERCATEGORIES,
	IC_BADCATEGORIES, 
	IC_GETITEMINFO,
	IC_GETRANGEINFO,
	IC_COLUMNCHECK,
	IC_CELLTYPE,
	IC_ADDCOLUMNS
};

enum handlingValues
{
	IC_USEVALUE = 0,
	IC_USEZERO,
	IC_SKIPVALUE,
	IC_CONVERT,
	IC_ERROR
};

- init
{	
	[super init];

	NXWriteDefault("Improv", "enableOakumPort", "Yes");
	
	sheet = (char *)malloc(1);
	model = (char *)malloc(1);
	strcpy(sheet, "");
	strcpy(model, "");

	emptyCellHandling = WB_ZERO;
	textCellHandling = WB_ERROR;
	return self;
}
	
- setCellHandlingForEmpty:(int)handlingForEmpty text:(int)handlingForText
{
	emptyCellHandling = handlingForEmpty;
	textCellHandling = handlingForText;
	return self;
}

- (int)doubleData:(double **)data fromColumn:(char *)columnName 
												dataCount:(int *)numRows
{
	return [self dataType:WB_DOUBLEDATA double:data float:NULL int:NULL 
					string:NULL fromColumn:columnName dataCount:numRows];
}

- (int)floatData:(float **)data fromColumn:(char *)columnName 
												dataCount:(int *)numRows
{
	return [self dataType:WB_FLOATDATA double:NULL float:data int:NULL 
					string:NULL fromColumn:columnName dataCount:numRows];
}

- (int)intData:(int **)data fromColumn:(char *)columnName 
												dataCount:(int *)numRows
{
	return [self dataType:WB_INTDATA double:NULL float:NULL int:data 
					string:NULL fromColumn:columnName dataCount:numRows];
}


- (int)stringData:(char ***)data fromColumn:(char *)columnName 
												dataCount:(int *)numRows
{
	return [self dataType:WB_STRINGDATA double:NULL float:NULL int:NULL 
					string:data fromColumn:columnName dataCount:numRows];
}
												
- (int)dataType:(int)dataType double:(double **)doubleData 
				float:(float **)floatData int:(int **)intData 
				string:(char ***)stringData fromColumn:(char *)columnName 
				dataCount:(int *)numRows
{
	char *selName, *viewName, *sheetName, *modelName, *lastRow, *firstRow;
	char *parent, **rowNames, cellName[100], *cellString, *parentName;
	char **categoryNames, *categoryList;
	imxSelType selType;
	imxCellType cellType;
	int imxReturnCode, myReturnCode=0, rowCount, rowIndex, categoryCount;
	int dataIndex;
	double cellValue;
	BOOL found;
	
	NX_DURING

	// make sure the named column really exists and get its category
		// get the current sheet and model
			if ((imxReturnCode = imxGetSelection(&selName, &selType, &viewName, 
												&sheetName, &modelName)) != 0)
				NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
			
			sheet = (char *)realloc(sheet, strlen(sheetName)+1);								
			model = (char *)realloc(model, strlen(modelName)+1);								
 			strcpy(sheet, sheetName);
 			strcpy(model, modelName);
		
		// make sure we can find the given column and that it is a column		
			if ((imxReturnCode = imxGetItemInfo(&parentName, &firstRow, 
								&lastRow, columnName, sheet, model)) != 0)
				NX_RAISE(IC_GETINFO, &imxReturnCode, NULL);
			if (parentName == NULL)
				NX_RAISE(IC_COLUMNCHECK, &imxReturnCode, NULL);
					
			parent = (char *)malloc(strlen(parentName) + 1);
			strcpy(parent, parentName);

			if ((imxReturnCode = imxGetCategoryList(&categoryList, sheet, 
															model)) != 0)
				NX_RAISE(IC_GETCATEGORIES, &imxReturnCode, NULL);
				
			[self getNames:&categoryNames fromList:categoryList 
													count:&categoryCount];
			for (rowIndex = 0, found = NO; (rowIndex < categoryCount) 
													&& !found; rowIndex++)
				if (strcmp(categoryNames[rowIndex], parent) == 0)
					found = YES;
			[self freeData:categoryNames count:categoryCount];										
			if (!found)
			{
				free(parent);
				NX_RAISE(IC_COLUMNCHECK, NULL, NULL);										
			}
			
	// get the list of row names
		if ((myReturnCode = [self getRows:&rowNames rowCount:&rowCount 
											forColumnCategory:parent]) != 0)
				NX_RAISE(IC_GETROWS, NULL, NULL);										
				
		free(parent);
									
	// get the data from the column
		switch (dataType)
		{
			case WB_DOUBLEDATA:
				*doubleData = (double *)malloc(rowCount * sizeof(double));
				break;
			case WB_FLOATDATA:
				*floatData = (float *)malloc(rowCount * sizeof(float));
				break;
			case WB_INTDATA:
				*intData = (int *)malloc(rowCount * sizeof(int));
				break;
			case WB_STRINGDATA:
				*stringData = (char **)malloc(rowCount * sizeof(char *));
				break;
		}
		for (rowIndex = 0, dataIndex = 0; rowIndex < rowCount; rowIndex++)
		{
			sprintf(cellName, "%s:%s", columnName, rowNames[rowIndex]);													
			if ((imxReturnCode = imxGetCellValue(cellName, &cellType, 
								&cellValue, &cellString, sheet, model)) != 0)
				NX_RAISE(IC_GETCELLVALUE, &imxReturnCode, &dataType);
			
			switch ([self handleCell:cellType forData:dataType])
			{
				case IC_SKIPVALUE:
						dataIndex--;
					break;
				case IC_USEVALUE:
					switch (dataType)
					{
						case WB_DOUBLEDATA:
							(*doubleData)[dataIndex] = cellValue;
							break;
						case WB_FLOATDATA:
							(*floatData)[dataIndex] = cellValue;
							break;
						case WB_INTDATA:
							(*intData)[dataIndex] = cellValue;
							break;
						case WB_STRINGDATA:
							(*stringData)[dataIndex] = (char *)malloc
													(strlen(cellString) + 1);
							strcpy((*stringData)[dataIndex], cellString);
							break;
					}
					break;
				case IC_USEZERO:
					switch (dataType)
					{
						case WB_DOUBLEDATA:
							(*doubleData)[dataIndex] = 0;
							break;
						case WB_FLOATDATA:
							(*floatData)[dataIndex] = 0;
							break;
						case WB_INTDATA:
							(*intData)[dataIndex] = 0;
							break;
						case WB_STRINGDATA:
							(*stringData)[dataIndex] = (char *)malloc(1);
							strcpy((*stringData)[dataIndex], "");
							break;
					}
					break;
				case IC_CONVERT:   // data type must be string to get this case
					(*stringData)[dataIndex] = (char *)malloc(15);
					sprintf((*stringData)[dataIndex], "%f", cellValue);
					[self deleteTrailingZeros:(*stringData)[dataIndex]];
					break;		
				case IC_ERROR:
					NX_RAISE(IC_CELLTYPE, NULL, NULL);
					break;
			}
			dataIndex++;			
		}
				
	// send back the number of rows 
		*numRows = dataIndex;

	// free stuff
		[self freeData:rowNames count:rowCount];
		
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETSELECTION:
				if (*(int *)NXLocalHandler.data1 == imxerr_NO_SELECTION)
					myReturnCode = WB_NOTHINGSELECTED;
				else
					myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETINFO:
				if (*(int *)NXLocalHandler.data1 == imxerr_ITEM_NOT_FOUND)
					myReturnCode = WB_NOSUCHCOLUMN;
				else
					myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETCATEGORIES:
				free(parent);
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETROWS:
				free(parent);
				break;
			case IC_COLUMNCHECK:
				myReturnCode = WB_NOSUCHCOLUMN;
				break;
			case IC_GETCELLVALUE:
				switch (dataType)
				{
					case WB_DOUBLEDATA: 
						free(*doubleData);
						break;
					case WB_FLOATDATA: 
						free(*floatData);
						break;
					case WB_INTDATA: 
						free(*intData);
						break;
					case WB_STRINGDATA: 
						[self freeData:*stringData count:dataIndex];
						break;
				}				
				[self freeData:rowNames count:rowCount];
				myReturnCode = WB_UNKNOWNERROR;
				break;				
			case IC_CELLTYPE:
				switch (dataType)
				{
					case WB_DOUBLEDATA: 
						free(*doubleData);
						break;
					case WB_FLOATDATA: 
						free(*floatData);
						break;
					case WB_INTDATA: 
						free(*intData);
						break;
					case WB_STRINGDATA: 
						[self freeData:*stringData count:dataIndex];
						break;	
				}			
				[self freeData:rowNames count:rowCount];
				myReturnCode = WB_WRONGCELLTYPE;
				break;				
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER
		
	return myReturnCode;
}

- (int)doubleData:(double ***)data fromSelectedColumnsCount:(int *)numColumns
								byRows:(BOOL)byRows dataCount:(int *)numRows
{
	return [self dataType:WB_DOUBLEDATA double:data float:NULL int:NULL 
				string:NULL fromSelectedColumnsCount:numColumns
				byRows:byRows dataCount:numRows];
}
												
- (int)floatData:(float ***)data fromSelectedColumnsCount:(int *)numColumns
								byRows:(BOOL)byRows dataCount:(int *)numRows													
{
	return [self dataType:WB_FLOATDATA double:NULL float:data int:NULL 
				string:NULL fromSelectedColumnsCount:numColumns
				byRows:byRows dataCount:numRows];
}
												
- (int)intData:(int ***)data fromSelectedColumnsCount:(int *)numColumns
								byRows:(BOOL)byRows dataCount:(int *)numRows													
{
	return [self dataType:WB_INTDATA double:NULL float:NULL int:data 
				string:NULL fromSelectedColumnsCount:numColumns
				byRows:byRows dataCount:numRows];
}
												
- (int)stringData:(char ****)data fromSelectedColumnsCount:(int *)numColumns
								byRows:(BOOL)byRows dataCount:(int *)numRows													
{
	return [self dataType:WB_STRINGDATA double:NULL float:NULL int:NULL 
				string:data fromSelectedColumnsCount:numColumns
				byRows:byRows dataCount:numRows];
}
																			
- (int)dataType:(int)dataType double:(double ***)doubleData 
				float:(float ***)floatData
				int:(int ***)intData 
				string:(char ****)stringData
				fromSelectedColumnsCount:(int *)numColumns
				byRows:(BOOL)byRows dataCount:(int *)numRows
{
	char *selectionName, *viewName, *sheetName, *modelName, cellName[100];
	char *parent, *selection, *cellString; 
	int itemCount, itemIndex, rowCount, rowIndex;
	int firstIndex, secondIndex, firstCount, secondCount;
	int imxReturnCode, myReturnCode = 0, dataIndex;
	imxSelType selectionType;
	imxCellType cellType;
	char *itemList, *selectedCategory, **itemNames, **rowNames;
	double cellValue;
	
	NX_DURING
	
	// find out what sort of thing is currently selected, should be item(s) 
		if ((imxReturnCode = imxGetSelection(&selectionName, &selectionType, 
									&viewName, &sheetName, &modelName)) != 0)
			NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
		if (selectionType != imxsel_ITEM)
			NX_RAISE(IC_SELECTIONTYPE, NULL, NULL);
				
		selection = (char *)malloc(strlen(selectionName) + 1);
		strcpy(selection, selectionName);
		strcpy(sheet, "");
		strcpy(model, "");
		
	// separate names of selected items and count them
		if ((imxReturnCode = imxGetItemRangeInfo(&parent, &itemList, selection, 
														sheet, model)) != 0)
			NX_RAISE(IC_GETRANGE, &imxReturnCode, NULL);
		
		free(selection);	
		selectedCategory = (char *)malloc(strlen(parent) + 1);
		strcpy(selectedCategory, parent);

		[self getNames:&itemNames fromList:itemList count:&itemCount];
			
	// get the list of row names
		if ((myReturnCode = [self getRows:&rowNames rowCount:&rowCount 
								forColumnCategory:selectedCategory]) != 0)
			NX_RAISE(IC_GETROWS, NULL, NULL);
									
		free(selectedCategory);
		
	// get the data from the cells and store it in an array
		if (byRows)
		{
			firstCount = rowCount;
			secondCount = itemCount;
		}
		else
		{
			firstCount = itemCount;
			secondCount = rowCount;
		}
		switch (dataType)
		{
			case WB_DOUBLEDATA:
				*doubleData = (double **)malloc(firstCount * sizeof(double *));
				break;
			case WB_FLOATDATA:
				*floatData = (float **)malloc(firstCount * sizeof(float *));
				break;
			case WB_INTDATA:
				*intData = (int **)malloc(firstCount * sizeof(int *));
				break;
			case WB_STRINGDATA:
				*stringData = (char ***)malloc(firstCount * sizeof(char **));
				break;
		}
		for (firstIndex = 0,dataIndex=0; firstIndex < firstCount; firstIndex++)
		{
			switch (dataType)
			{
				case WB_DOUBLEDATA:
					(*doubleData)[dataIndex] = (double *)malloc
												(secondCount * sizeof(double));
					break;
				case WB_FLOATDATA:
					(*floatData)[dataIndex] = (float *)malloc
												(secondCount * sizeof(float));
					break;
				case WB_INTDATA:
					(*intData)[dataIndex] = (int *)malloc
												(secondCount * sizeof(int));
					break;
				case WB_STRINGDATA:
					(*stringData)[dataIndex] = (char **)malloc
												(secondCount * sizeof(char *));
				break;
			}
			for (secondIndex = 0; secondIndex < secondCount; secondIndex++)
			{
				if (byRows)
				{
					rowIndex = firstIndex;
					itemIndex = secondIndex;
				}
				else
				{
					rowIndex = secondIndex;
					itemIndex = firstIndex;
				}
				sprintf(cellName, "%s:%s", itemNames[itemIndex], 
														rowNames[rowIndex]);
														
				if ((imxReturnCode = imxGetCellValue(cellName, &cellType, 
								&cellValue, &cellString, sheet, model)) != 0)
					NX_RAISE(IC_GETCELLVALUE, NULL, NULL);

				switch ([self handleCell:cellType forData:dataType])
				{
					case IC_SKIPVALUE:
							dataIndex--;
							secondIndex = secondCount;
						break;
					case IC_USEVALUE:
						switch (dataType)
						{
							case WB_DOUBLEDATA:
							 	(*doubleData)[dataIndex][secondIndex] 
																= cellValue;
								break;
							case WB_FLOATDATA:
								(*floatData)[dataIndex][secondIndex] 
																= cellValue;
								break;
							case WB_INTDATA:
								(*intData)[dataIndex][secondIndex] = cellValue;
								break;
							case WB_STRINGDATA:
								(*stringData)[dataIndex][secondIndex] 
									= (char *)malloc(strlen(cellString) + 1);
								strcpy((*stringData)[dataIndex][secondIndex], 
																cellString);
								break;
						}
						break;
					case IC_USEZERO:
						switch (dataType)
						{
							case WB_DOUBLEDATA:
								(*doubleData)[dataIndex][secondIndex] = 0;
								break;
							case WB_FLOATDATA:
								(*floatData)[dataIndex][secondIndex] = 0;
								break;
							case WB_INTDATA:
								(*intData)[dataIndex][secondIndex] = 0;
								break;
							case WB_STRINGDATA:
								(*stringData)[dataIndex][secondIndex] 
														= (char *)malloc(1);
								strcpy((*stringData)[dataIndex][secondIndex], 
																		"");
								break;
						}
						break;
					case IC_CONVERT:    // data type must be string to get this
						(*stringData)[dataIndex][secondIndex] 
													= (char *)malloc(15);
						sprintf((*stringData)[dataIndex][secondIndex], "%f", 
																cellValue);
						[self deleteTrailingZeros:
										(*stringData)[dataIndex][secondIndex]];									
						break;		
					case IC_ERROR:
						NX_RAISE(IC_CELLTYPE, NULL, NULL);
						break;
				}
			}
			dataIndex++;			
		}
				
	// send back the number of rows and columns
		if (byRows)
		{
			*numColumns = itemCount;
			*numRows = dataIndex;
		}
		else
		{
			*numColumns = dataIndex;
			*numRows = rowCount;
		}
		
	// free stuff
		[self freeData:itemNames count:itemCount];
		[self freeData:rowNames count:rowCount];
	
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETSELECTION:
				if (*(int *)NXLocalHandler.data1 == imxerr_NO_SELECTION)
					myReturnCode = WB_NOTHINGSELECTED;
				else
					myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_SELECTIONTYPE:
				myReturnCode = WB_WRONGTYPEOFSELECTION;
				break;
			case IC_GETRANGE:
				free(selection);
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETROWS:
				[self freeData:itemNames count:itemCount];
				[self freeData:rowNames count:rowCount];
				break;
			case IC_GETCELLVALUE:
				[self freeData:itemNames count:itemCount];
				[self freeData:rowNames count:rowCount];
				switch (dataType)
				{
					case WB_DOUBLEDATA: 
						[self freeData:*doubleData count:dataIndex];
						break;
					case WB_FLOATDATA: 
						[self freeData:*floatData count:dataIndex];
						break;
					case WB_INTDATA: 
						[self freeData:*intData count:dataIndex];
						break;
					case WB_STRINGDATA: 
						[self freeStringData:*stringData 
											counts:dataIndex-1 :secondCount];
						if (secondIndex > 0)
							[self freeData:(*stringData)[dataIndex] 
														count:secondIndex];
						break;
				}				
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_CELLTYPE:
				[self freeData:itemNames count:itemCount];
				[self freeData:rowNames count:rowCount];
				switch (dataType)
				{
					case WB_DOUBLEDATA: 
						[self freeData:*doubleData count:dataIndex];
						break;
					case WB_FLOATDATA: 
						[self freeData:*floatData count:dataIndex];
						break;
					case WB_INTDATA: 
						[self freeData:*intData count:dataIndex];
						break;
					case WB_STRINGDATA: 
						[self freeStringData:*stringData 
											counts:dataIndex-1 :secondCount];
						if (secondIndex > 0)
							[self freeData:(*stringData)[dataIndex] 
														count:secondIndex];
						break;
				}				
				myReturnCode = WB_WRONGCELLTYPE;
				break;				
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER
	
	return myReturnCode;
}

- (int)fillColumnsCount:(int)numColumns startingAt:(char *)columnName 
						withDoubleData:(double **)data 
						byRows:(BOOL)byRows dataCount:(int)numRows
{
	return [self fillColumnsCount:numColumns startingAt:columnName 
					withDataType:WB_DOUBLEDATA double:data
					float:NULL int:NULL string:NULL 
					byRows:byRows dataCount:numRows];
}

- (int)fillColumnsCount:(int)numColumns startingAt:(char *)columnName 
						withFloatData:(float **)data 
						byRows:(BOOL)byRows dataCount:(int)numRows
{
	return [self fillColumnsCount:numColumns startingAt:columnName 
					withDataType:WB_FLOATDATA double:NULL
					float:data int:NULL string:NULL 
					byRows:byRows dataCount:numRows];
}

- (int)fillColumnsCount:(int)numColumns startingAt:(char *)columnName 
						withIntData:(int **)data 
						byRows:(BOOL)byRows dataCount:(int)numRows
{
	return [self fillColumnsCount:numColumns startingAt:columnName 
					withDataType:WB_INTDATA double:NULL
					float:NULL int:data string:NULL 
					byRows:byRows dataCount:numRows];
}

- (int)fillColumnsCount:(int)numColumns startingAt:(char *)columnName 
					withStringData:(char ***)data 
						byRows:(BOOL)byRows dataCount:(int)numRows
{
	return [self fillColumnsCount:numColumns startingAt:columnName 
					withDataType:WB_STRINGDATA double:NULL
					float:NULL int:NULL string:data 
					byRows:byRows dataCount:numRows];
}

- (int)fillColumnsCount:(int)numColumns startingAt:(char *)columnName 
					withDataType:(int)dataType double:(double **)doubleData
					float:(float **)floatData int:(int **)intData
					string:(char ***)stringData 
					byRows:(BOOL)byRows dataCount:(int)numRows
{
	char *selName, *viewName, *sheetName, *modelName, *lastRow, *firstRow;
	char *parent, **rowNames, cellName[500], *parentName, *categoryList;
	char **categoryNames, *cellString, *rowCategory, **columnNames;
	char **fillColumnNames;
	imxSelType selType;
	imxCellType cellType;
	int imxReturnCode, myReturnCode = 0, rowCount, rowIndex, categoryCount;
	int colIndex, colCount;
	BOOL found;
	double cellValue;
	
	NX_DURING
	
	// make sure the starting column really exists and get its category
		// get the current sheet and model
			if ((imxReturnCode = imxGetSelection(&selName, &selType, &viewName, 
												&sheetName, &modelName)) != 0)
				NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
			
			sheet = (char *)realloc(sheet, strlen(sheetName)+1);								
			model = (char *)realloc(model, strlen(modelName)+1);								
 			strcpy(sheet, sheetName);
 			strcpy(model, modelName);
		
		// make sure we can find the given column and that it is a column		
			if ((imxReturnCode = imxGetItemInfo(&parentName, &firstRow, 
								&lastRow, columnName, sheet, model)) != 0)
				NX_RAISE(IC_GETINFO, &imxReturnCode, NULL);
			if (parentName == NULL)
				NX_RAISE(IC_COLUMNCHECK, &imxReturnCode, NULL);
					
			parent = (char *)malloc(strlen(parentName) + 1);
			strcpy(parent, parentName);

			if ((imxReturnCode = imxGetCategoryList(&categoryList, sheet, 
															model)) != 0)
				NX_RAISE(IC_GETCATEGORIES, &imxReturnCode, NULL);
				
			[self getNames:&categoryNames fromList:categoryList 
													count:&categoryCount];
			for (rowIndex = 0, found = NO; rowIndex < categoryCount; 
																rowIndex++)
				if (strcmp(categoryNames[rowIndex], parent) == 0)
					found = YES;
				else
				{
					rowCategory = (char *)malloc
									(strlen(categoryNames[rowIndex]) + 1);
					strcpy(rowCategory, categoryNames[rowIndex]);
				}
									
			[self freeData:categoryNames count:categoryCount];										
			if (!found)
			{
				free(parent);
				NX_RAISE(IC_COLUMNCHECK, NULL, NULL);										
			}
			
	// get the list of rows for the given column
		if ((myReturnCode = [self getRows:&rowNames rowCount:&rowCount 
											forColumnCategory:parent]) != 0)
			NX_RAISE(IC_GETROWS, NULL, NULL);										
		
	// if there aren't enough rows for the data, add some at the end
		if (rowCount < numRows)
			if ((myReturnCode = [self addColumns:NULL count:numRows-rowCount 
									afterColumn:rowNames[rowCount-1]]) != 0)
				NX_RAISE(IC_ADDCOLUMNS, NULL, NULL);										

		[self freeData:rowNames count:rowCount];
		if ((myReturnCode = [self getRows:&rowNames rowCount:&rowCount 
											forColumnCategory:parent]) != 0)
			NX_RAISE(IC_GETROWS, NULL, NULL);										
		
	// get the list of column names, make sure there are enough
		if ((myReturnCode = [self getRows:&columnNames rowCount:&colCount 
										forColumnCategory:rowCategory]) != 0)
			NX_RAISE(IC_GETCOLUMNS, NULL, NULL);										

		for (colIndex = 0, found = NO; (colIndex < colCount) && !found;
														colIndex++)
			if (strcmp(columnNames[colIndex], columnName) == 0)
			{
				fillColumnNames = columnNames + colIndex;
				found = YES;
			}
		free(parent);
		if (colCount - colIndex	+ 1 < numColumns)
			NX_RAISE(IC_NUMBERCOLUMNS, NULL, NULL);										
				
	// insert the data into the columns
		if (dataType == WB_STRINGDATA)
		{
				cellType = cval_TEXT;
				cellValue = 0;
		}
		else
		{
			cellType = cval_NUMBER;
			cellString = (char *)malloc(1);
			strcpy(cellString, "");
		}
		for (rowIndex = 0; rowIndex < numRows; rowIndex++)
			for (colIndex = 0; colIndex < numColumns; colIndex++)
			{
				switch (dataType)
				{
					case WB_DOUBLEDATA:
						cellValue = doubleData[rowIndex][colIndex];
						break;
					case WB_FLOATDATA:
						cellValue = floatData[rowIndex][colIndex];
						break;
					case WB_INTDATA:
						cellValue = intData[rowIndex][colIndex];
						break;
					case WB_STRINGDATA:
						cellString = (char *)malloc
								(strlen(stringData[rowIndex][colIndex]) + 1);
						strcpy(cellString, stringData[rowIndex][colIndex]);		
						break;
				}	 
				sprintf(cellName, "%s:%s", fillColumnNames[colIndex], 
														rowNames[rowIndex]);
				if ((imxReturnCode = imxSetCellValue(cellName, cellType, 
								cellValue, cellString, sheet, model)) != 0)
								
					NX_RAISE(IC_SETCELLVALUE, &imxReturnCode, NULL);
			}
		if ((imxReturnCode = imxUpdateModel(model)) != 0)
			NX_RAISE(IC_UPDATE, &imxReturnCode, NULL);
	
	// free stuff
		[self freeData:rowNames count:rowCount];
		[self freeData:columnNames count:colCount];
			
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETSELECTION:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETINFO:
				if (*(int *)NXLocalHandler.data1 == imxerr_ITEM_NOT_FOUND)
					myReturnCode = WB_NOSUCHCOLUMN;
				else
					myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETCATEGORIES:
				free(parent);
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETROWS:
				free(parent);
				break;
			case IC_ADDCOLUMNS:
				free(parent);
				[self freeData:rowNames count:rowCount];
				break;
			case IC_GETCOLUMNS:
				free(parent);
				[self freeData:rowNames count:rowCount];
				break;
			case IC_COLUMNCHECK:
				myReturnCode = WB_NOSUCHCOLUMN;
				break;
			case IC_NUMBERCOLUMNS:
				[self freeData:rowNames count:rowCount];
				[self freeData:columnNames count:colCount];
				myReturnCode = WB_NOTENOUGHCOLUMNS;
				break;
			case IC_SETCELLVALUE:
			case IC_UPDATE:
				[self freeData:rowNames count:rowCount];
				[self freeData:columnNames count:colCount];
				myReturnCode = WB_UNKNOWNERROR;
				break;
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER

	return myReturnCode;
}


- (int)addColumns:(char **)newColumns count:(int)numColumns 
									afterColumn:(char *)existingColumn
{
	imxSelType selectionType = imxsel_ITEM, oldType;
	char *viewName, *sheetName, *modelName, *selection, *view, **columnNames;
	int imxReturnCode, myReturnCode = 0, numCols, colIndex;
	
	NX_DURING
	
	// get the view, sheet, and model
		if ((imxReturnCode = imxGetSelection(&selection, &oldType, 
								&viewName, &sheetName, &modelName)) != 0)
			NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
	
		view = (char *)malloc(strlen(viewName)+1);								
		sheet = (char *)realloc(sheet, strlen(sheetName)+1);								
		model = (char *)realloc(model, strlen(modelName)+1);								
 		strcpy(view, viewName);
 		strcpy(sheet, sheetName);
 		strcpy(model, modelName);
	
	// select it the given column
		if ((imxReturnCode = imxSetSelection(existingColumn, selectionType, 
												view, sheet, model)) != 0)
			NX_RAISE(IC_SETSELECTION, &imxReturnCode, NULL);
		free(view);
	
	// create the new columns										
		if ((imxReturnCode = imxAddItems(NULL, numColumns, ipos_AFTER)) != 0)
			NX_RAISE(IC_ADDITEMS, NULL, NULL);
	
	// rename the new columns
		if (newColumns)
		{
			[self getSelectedColumnNames:&columnNames count:&numCols];
		
			for (colIndex = 0; colIndex < numCols; colIndex++)
				if ((imxReturnCode = imxRename(newColumns[colIndex], 
									imxsel_ITEM, columnNames[colIndex], 
														sheet, model)) != 0)
					NX_RAISE(IC_RENAME, &imxReturnCode, NULL);
			[self freeData:columnNames count:numCols];								
		}
		
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETSELECTION:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_SETSELECTION:
				free(view);
				if (*(int *)NXLocalHandler.data1 == imxerr_NO_SELECTION)
					myReturnCode = WB_NOSUCHCOLUMN;
				else
					myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_ADDITEMS:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_RENAME:
				[self freeData:columnNames count:numCols];								
				myReturnCode = WB_UNKNOWNERROR;
				break;
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER
	
	return myReturnCode;
}

- (int)getSelectedColumnNames:(char ***)columnNames count:(int *)numColumns
{
	imxSelType selectionType;
	char *viewName, *sheetName, *modelName, *selectionName, *parentName;
	int imxReturnCode, myReturnCode = 0;
	char *columnList, *selection;
	
	NX_DURING
	
	// get the selection information
		if ((imxReturnCode = imxGetSelection(&selectionName, &selectionType,
								&viewName, &sheetName, &modelName)) != 0)
			NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
	
		sheet = (char *)realloc(sheet, strlen(sheetName)+1);								
		model = (char *)realloc(model, strlen(modelName)+1);								
		selection = (char *)malloc(strlen(selectionName)+1);								
 		strcpy(sheet, sheetName);
 		strcpy(model, modelName);
 		strcpy(selection, selectionName);
	
	// get a list of the names in the selected range
		if (selectionType == imxsel_ITEM)
		{
			if ((imxReturnCode = imxGetItemRangeInfo(&parentName, 
						&columnList, selection, sheet, model)) != 0)
				NX_RAISE(IC_GETINFO, &imxReturnCode, NULL);

			[self getNames:columnNames fromList:columnList count:numColumns];
		}		
		else
			NX_RAISE(IC_SELECTIONTYPE, &imxReturnCode, NULL);
	
	free(selection);								
	
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETSELECTION:
				if (*(int *)NXLocalHandler.data1 == imxerr_NO_SELECTION)
					myReturnCode = WB_NOTHINGSELECTED;
				else
					myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_GETINFO:
				free(selection);
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_SELECTIONTYPE:
				free(selection);
				myReturnCode = WB_WRONGTYPEOFSELECTION;
				break;
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER
	
	return myReturnCode;
}

- (int)makeNewWorksheet:(char *)worksheetName numRows:(int)numRows 
				numCols:(int)numColumns rowNames:(char **)rowNames 
				colNames:(char **)columnNames
{
	imxSelType selectionType;
	char *viewName, *sheetName, *modelName, *selection;
	int imxReturnCode, myReturnCode = 0;
	
	NX_DURING
	
	// return if number of rows or columns is zero
		if ((numRows <= 0) || (numColumns <= 0))
			NX_RAISE(IC_BADPARAMETERS, NULL, NULL);
			
	// create the new worksheet, rename it, get the sheet and model names	
		if ((imxReturnCode = imxCmdExec(imxcmd_NEWSHEET)) != 0)
			NX_RAISE(IC_NEWSHEET, &imxReturnCode, NULL);

		if ((imxReturnCode = imxGetSelection(&selection, &selectionType,
								&viewName, &sheetName, &modelName)) != 0)
			NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
		sheet = (char *)realloc(sheet, strlen(sheetName)+1);								
		model = (char *)realloc(model, strlen(modelName)+1);								
		strcpy(sheet, sheetName);
 		strcpy(model, modelName);

		if (worksheetName)
		{
			if ((imxReturnCode = imxRename(worksheetName, imxsel_SHEET,
												sheet, sheet, model)) != 0)
				NX_RAISE(IC_RENAME, &imxReturnCode, NULL);

			if ((imxReturnCode = imxGetSelection(&selection, &selectionType,
								&viewName, &sheetName, &modelName)) != 0)
				NX_RAISE(IC_GETSELECTION, &imxReturnCode, NULL);
			sheet = (char *)realloc(sheet, strlen(sheetName)+1);								
			model = (char *)realloc(model, strlen(modelName)+1);								
			strcpy(sheet, sheetName);
 			strcpy(model, modelName);
		}
		
	// add and name columns
		if (numColumns > 1)
			if (columnNames)
			{
				if ((myReturnCode = [self addColumns:columnNames+1 
									count:numColumns-1 afterColumn:"B1"]) != 0)
					NX_RAISE(IC_ADDCOLUMNS, NULL, NULL);
			}
			else
				if ((myReturnCode = [self addColumns:NULL count:numColumns-1 
													afterColumn:"B1"]) != 0)
					NX_RAISE(IC_ADDCOLUMNS, NULL, NULL);
			
		if (columnNames)
			if ((imxReturnCode = imxRename(columnNames[0], imxsel_ITEM,
												"B1", sheet, model)) != 0)
				NX_RAISE(IC_RENAME, &imxReturnCode, NULL);

	// add rows and name rows
		if (numRows > 1)
			if (rowNames)
			{
				if ((myReturnCode = [self addColumns:rowNames+1 count:numRows-1 
													afterColumn:"A1"]) != 0)
					NX_RAISE(IC_ADDCOLUMNS, NULL, NULL);
			}
			else
				if ((myReturnCode = [self addColumns:NULL count:numRows-1 
											afterColumn:"A1"]) != 0)
					NX_RAISE(IC_ADDCOLUMNS, NULL, NULL);
			
		if (rowNames)
			if ((imxReturnCode = imxRename(rowNames[0], imxsel_ITEM,
												"A1", sheet, model)) != 0)
				NX_RAISE(IC_RENAME, &imxReturnCode, NULL);
	
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETSELECTION:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_BADPARAMETERS:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_ADDITEMS:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_NEWSHEET:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_RENAME:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_ADDCOLUMNS:
				break;
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER
	
	return myReturnCode;
}

- getNames:(char ***)names fromList:(char *)list count:(int *)numNames
{
	int nameIndex;
	char *nameStart, *nameEnd;
	
	// splits a \n-separated list into separate strings
	
	for ((*numNames) = 0, nameStart = list; *nameStart;
					nameStart = strchr(nameStart, '\n') + 1, (*numNames)++);
				
	*names = (char **)malloc((*numNames) * sizeof(char *));
	nameStart = list;
	for (nameIndex = 0; nameIndex < *numNames; nameIndex++)
	{
		nameEnd = strchr(nameStart, '\n');
		(*names)[nameIndex] = (char *)malloc(nameEnd - nameStart + 1);
		strncpy((*names)[nameIndex], nameStart, nameEnd - nameStart);
		(*names)[nameIndex][nameEnd - nameStart] = '\0';
		nameStart = nameEnd + 1;
	}

	return self;
}

- (int)getRows:(char ***)rowList rowCount:(int *)numRows 
									forColumnCategory:(char *)columnCategory
{
	char *lastRow, *firstRow;
	char *categoryList, **categoryNames, *rowCategory, *parentName; 
	char *rowRange, *itemList;
	int imxReturnCode, myReturnCode = 0, categoryCount;

	NX_DURING
	
	// get a list of all the categories, make sure there aren't more than two
		if ((imxReturnCode = imxGetCategoryList(&categoryList, sheet, model)) 
																		!= 0)
			NX_RAISE(IC_GETCATEGORIES, &imxReturnCode, NULL);
															
		[self getNames:&categoryNames fromList:categoryList 
														count:&categoryCount];
		if (categoryCount > 2)
			NX_RAISE(IC_NUMBERCATEGORIES, NULL, NULL);
	
	// find which category contains the given item and which doesn't		
		if (strcmp(categoryNames[0], columnCategory) == 0)
			rowCategory = categoryNames[1];
		else
			if (strcmp(categoryNames[1], columnCategory) == 0)
				rowCategory = categoryNames[0];
			else
				NX_RAISE(IC_BADCATEGORIES, NULL, NULL);
				
	// for the non-given category, get a list of the items in it
		if ((imxReturnCode = imxGetItemInfo(&parentName, &firstRow, &lastRow, 
											rowCategory, sheet, model)) != 0)
			NX_RAISE(IC_GETITEMINFO, &imxReturnCode, NULL);
		[self freeData:categoryNames count:categoryCount];

		rowRange = (char *)malloc(strlen(firstRow) + strlen(lastRow) + 3);
		strcpy(rowRange, firstRow);
		strcat(rowRange, "..");
		strcat(rowRange, lastRow);
		
		if ((imxReturnCode = imxGetItemRangeInfo(&parentName, &itemList, 
											rowRange, sheet, model)) != 0)
			NX_RAISE(IC_GETRANGEINFO, &imxReturnCode, NULL);

		free(rowRange);
		[self getNames:rowList fromList:itemList count:numRows];
	
	NX_HANDLER
		switch (NXLocalHandler.code)
		{
			case IC_GETCATEGORIES:
				myReturnCode = WB_UNKNOWNERROR;
				break;
			case IC_NUMBERCATEGORIES:
				myReturnCode = WB_TOOMANYCATEGORIES;
				[self freeData:categoryNames count:categoryCount];
				break;
			case IC_BADCATEGORIES:
				myReturnCode = WB_NOSUCHCOLUMN;
				[self freeData:categoryNames count:categoryCount];
				break;
			case IC_GETITEMINFO:
				myReturnCode = WB_UNKNOWNERROR;
				[self freeData:categoryNames count:categoryCount];
				break;
			case IC_GETRANGEINFO:
				myReturnCode = WB_UNKNOWNERROR;
				free(rowRange);
				break;
			default:
				NX_RERAISE();
		}
	NX_ENDHANDLER

	return myReturnCode;
}


- (int)handleCell:(int)cellType forData:(int)dataType
{
	int handleAs = IC_ERROR;
	
	if (dataType == WB_STRINGDATA)
		switch (cellType)
		{
			case cval_TEXT:
				handleAs = IC_USEVALUE;
				break;
			case cval_EMPTY:
				handleAs = IC_USEZERO;
				break;
			case cval_NUMBER:
				handleAs = IC_CONVERT;
				break;
			default:
				handleAs = IC_ERROR;
		}
	else    			// data type is numeric
		switch (cellType)
		{
			case cval_NUMBER:
				handleAs = IC_USEVALUE;
				break;
			case cval_TEXT:
				switch (textCellHandling)
				{
					case WB_SKIP:
						handleAs = IC_SKIPVALUE;
						break;
					case WB_ZERO:
						handleAs = IC_USEZERO;
						break;
					case WB_ERROR:
						handleAs = IC_ERROR;
				}
				break;
			case cval_EMPTY:
				switch (emptyCellHandling)
				{
					case WB_SKIP:
						handleAs = IC_SKIPVALUE;
							break;
					case WB_ZERO:
						handleAs = IC_USEZERO;
						break;
					case WB_ERROR:
						handleAs = IC_ERROR;
				}
				break;
			default:
				handleAs = IC_ERROR;
		}
	return handleAs;
}

- deleteTrailingZeros:(char *)formattedNumber
{
	if (strchr(formattedNumber, '.'))
	{
		while (formattedNumber[strlen(formattedNumber)-1] == '0')
			formattedNumber[strlen(formattedNumber)-1] = '\0';
		
		if (formattedNumber[strlen(formattedNumber)-1] == '.')
			formattedNumber[strlen(formattedNumber)-1] = '\0';
	}	
	return self;
}

- freeData:(void *)data count:(int)count
{
	int index;
	
	for (index = 0; index < count; index++)
		free(((char **)data)[index]);
	free(data);

	return self;
}

- freeStringData:(void *)data counts:(int)count1 :(int)count2
{
	int index1, index2;
	
	for (index1 = 0; index1 < count1; index1++)
		for (index2 = 0; index2 < count2; index2++)
			free(((char ***)data)[index1][index2]);
			
	for (index1 = 0; index1 < count1; index1++)
		free(((char ***)data)[index1]);
			
	free(data);

	return self;
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.