This is GeoClient.m in view mode; [Download] [Up]
// GeoClient.m // By Charles G. Fleming, Educational Computing Services, 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 grant from the Vira Heinz Endowment. #import "GeoClient.h" #import "Subprocess.h" #import <appkit/Panel.h> #import <appkit/Application.h> #import <appkit/TextField.h> #import <appkit/PopUpList.h> #import <appkit/MenuCell.h> #import <appkit/ScrollView.h> #import <appkit/Text.h> #import <appkit/Button.h> #import <objc/Storage.h> #import <objc/Object.h> #import <strings.h> #import <string.h> #import <stdlib.h> @implementation GeoClient int stuffer(struct dataRecord* , char *); - appDidInit:sender { subprocess = [[Subprocess allocFromZone:[self zone]] init:"/bin/csh" withDelegate:self andPtySupport:YES andStdErr:YES]; [subprocess send:"telnet martini.eecs.umich.edu 3000"]; records = [[Storage allocFromZone:[self zone]] initCount:10 elementSize:sizeof(struct dataRecord) description:"{[50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c]}"]; // Set up the pull down lists. statesPullDownList = [statesPullDownListButton target]; [statesPullDownList setTarget:self]; [statesPullDownList setAction:@selector(selectState:)]; moreStatesPullDownList = [moreStatesPullDownListButton target]; [moreStatesPullDownList setTarget:self]; [moreStatesPullDownList setAction:@selector(selectState:)]; // Display a message until login is complete. [NXApp runModalFor:loginMessageWindow]; return self; } // Query the database for information on a particular city. - query:sender { char *cityAndState; // Enable the button only after the query is finished. [sender setEnabled:NO]; // So that we will receive text did change messages from our text fields. [self makeFirstResponder:self]; // Reset the pull down list, clear the data records in the storage object and clear the // text fields. [self resetPullDownList]; [records empty]; [self clearTextFields]; cityAndState = malloc(strlen([cityTextField stringValue])+strlen([stateTextField stringValue])+3); // Build the query string for the database server. The query string will be "City, State" or "City". if(strlen([stateTextField stringValue]) > 0) sprintf(cityAndState, "%s, %s", [cityTextField stringValue],[stateTextField stringValue]); else strcpy(cityAndState, [cityTextField stringValue]); // Send the query to the subprocess object. [subprocess send:cityAndState]; // Searching message window. [searchingMessageWindow makeKeyAndOrderFront:self]; free(cityAndState); return self; } // Put the information for the state selected from the pull down list into the text fields. - selectState:sender { int tag; struct dataRecord *newRecord; // Get the record for the selected state. tag = [[sender selectedCell] tag]; newRecord = (struct dataRecord *)[records elementAt:tag]; // Put the information into the text fields. [stateTextField setStringValue:newRecord->stateName]; [countyCodeTextField setStringValue:newRecord->countyCode]; [countyNameTextField setStringValue:newRecord->countyName]; [stateAbbrTextField setStringValue:newRecord->stateAbbr]; [stateNameTextField setStringValue:newRecord->stateName]; [nationAbbrTextField setStringValue:newRecord->nationAbbr]; [nationNameTextField setStringValue:newRecord->nationName]; [areaCodeTextField setStringValue:newRecord->areaCode]; [elevationTextField setStringValue:newRecord->elevation]; [featureCodeTextField setStringValue:newRecord->featureCode]; [featureNameTextField setStringValue:newRecord->featureName]; [latitudeTextField setStringValue:newRecord->latitude]; [longitudeNameTextField setStringValue:newRecord->longitude]; [censusTextField setStringValue:newRecord->census]; [remarkTextField setStringValue:newRecord->remark]; [timeZoneTextField setStringValue:newRecord->timeZone]; [[zipScrollView docView] setText:newRecord->zip]; return self; } // Clear all of the text fields. - clearTextFields { [countyCodeTextField setStringValue:""]; [countyNameTextField setStringValue:""]; [stateAbbrTextField setStringValue:""]; [stateNameTextField setStringValue:""]; [nationAbbrTextField setStringValue:""]; [nationNameTextField setStringValue:""]; [areaCodeTextField setStringValue:""]; [elevationTextField setStringValue:""]; [featureCodeTextField setStringValue:""]; [featureNameTextField setStringValue:""]; [latitudeTextField setStringValue:""]; [longitudeNameTextField setStringValue:""]; [censusTextField setStringValue:""]; [remarkTextField setStringValue:""]; [timeZoneTextField setStringValue:""]; [[zipScrollView docView] setText:""]; return self; } // Remove the entries from the pull down lists. - resetPullDownList { int statesCount, state; statesCount = [statesPullDownList count]; for(state = statesCount-1; state > 0; state--) [statesPullDownList removeItemAt:state]; statesCount = [moreStatesPullDownList count]; for(state = statesCount-1; state > 0; state--) [moreStatesPullDownList removeItemAt:state]; return self; } - textDidChange:textObject { TextField *textField; // Clear the text fields. [self clearTextFields]; // Reset the pull down list. [self resetPullDownList]; [statesPullDownListButton setEnabled:NO]; [moreStatesPullDownListButton setEnabled:NO]; // Emtpy all city records. [records empty]; textField = [textObject delegate]; if(textField == cityTextField) [stateTextField setStringValue:""]; return self; } - appWillTerminate:sender { [subprocess send:"bye"]; [subprocess terminate:self]; return self; } // Process the output from the subprocess object. - subprocessOutput:(char *)buffer { static char *message; char *period, *start, *end; static BOOL firstTime = YES; static BOOL login = YES; int cityCount; MenuCell *menuCell; // Malloc a message buffer only once. if(firstTime) { message = malloc(10000); firstTime = NO; } // Process a login. strcat(message, buffer); if(login) { period = rindex(message, '.'); if(period ) if(*(period - 1) == '\n') { [loginMessageWindow close]; [NXApp stopModal]; [queryButton setEnabled:YES]; bzero(message, strlen(message)+1); *(message+1) = '\0'; login = NO; } } // Process the data returned from the database server. else { // Process the records only after all buffers have arrived. if(strstr(message, "\n.")) { // No cities found. if(!strstr(message, "\n0")) { bzero(message, strlen(message)+1); [searchingMessageWindow close]; [queryButton setEnabled:YES]; } // Process the cities. else { // Process all records but the last one (states). cityCount = 0; start = strstr(message, "\n0")+1; while(end = strstr(start, "\n0")) { *end = '\0'; // Clear out the record and then stuff all of the information into it. bzero(&record, sizeof(struct dataRecord)); if(stuffer(&record, start)) { if(cityCount < 39) { // Check for duplicate states. The data base has some duplicates. while( [statesPullDownList indexOfItem:record.stateName] != -1) strcat(record.stateName, " "); // Get the state and add a button to the pulldown list. menuCell = [statesPullDownList addItem:record.stateName]; [menuCell setTag:cityCount]; } else { // Check for duplicate states. The data base has some duplicates. while( [moreStatesPullDownList indexOfItem:record.stateName] != -1) strcat(record.stateName, " "); // Get the state and add a button to the pulldown list. menuCell = [moreStatesPullDownList addItem:record.stateName]; [menuCell setTag:cityCount]; } [records addElement:&record]; cityCount++; } start = end+1; } // Now process the last record (state). end = strstr(start, "\n."); *end = '\0'; // Clear out the record and then stuff all of the information into it. bzero(&record, sizeof(struct dataRecord)); if(stuffer(&record, start)) { if(cityCount < 39) { // Check for duplicate states. The data base has some duplicates. while( [statesPullDownList indexOfItem:record.stateName] != -1) strcat(record.stateName, " "); // Get the state and add a button to the pulldown list. menuCell = [statesPullDownList addItem:record.stateName]; [menuCell setTag:cityCount]; } else { // Check for duplicate states. The data base has some duplicates. while( [moreStatesPullDownList indexOfItem:record.stateName] != -1) strcat(record.stateName, " "); // Get the state and add a button to the pulldown list. menuCell = [moreStatesPullDownList addItem:record.stateName]; [menuCell setTag:cityCount]; } [records addElement:&record]; cityCount++; } if(cityCount > 0) [statesPullDownListButton setEnabled:YES]; else [statesPullDownListButton setEnabled:NO]; if(cityCount > 38) [moreStatesPullDownListButton setEnabled:YES]; else [moreStatesPullDownListButton setEnabled:NO]; bzero(message, strlen(message)+1); [searchingMessageWindow close]; [queryButton setEnabled:YES]; } } } return self; } // This is sent when the connection is broken, or when we quit the application. - subprocessDone { NXRunAlertPanel("BYE", "You are no longer connected to the remote machine.", NULL, NULL, NULL); return self; } // Display any error messages received. - subprocessError:(const char *)errorString { [errorMessageWindow makeKeyAndOrderFront:self]; [[errorMessageScrollView docView] setText:errorString]; return self; } // Takes each string, corresponding to a city and specific state, and puts the information // into a record. int stuffer(struct dataRecord *record , char *entry) { char *zero, *start, *copy, *linefeed, *string, *nextSpace; BOOL stateFound = NO, done = NO; int firstChar, blank; copy = malloc(strlen(entry) + 1); strcpy(copy, entry); // Find the city field and put it into the structure. zero = index(copy, '0')+2; linefeed = index(zero, '\n'); *(linefeed) = '\0'; strcpy(record->city, zero); // Get the fields and put them into the structure. start = linefeed +1; while(!done) { // Put a null terminator at the end of each line before processing the line. linefeed = index(start, '\n'); *linefeed = '\0'; // Based on the first character, put the field into the structure. firstChar = (int)*(start); switch(firstChar) { case '1': // Get the county code. string = index(start, ' ')+1; nextSpace = index(string, ' '); *nextSpace = '\0'; strcpy(record->countyCode, string); // Get the county name. string = nextSpace+1; strcpy(record->countyName, string); break; case '2': stateFound = YES; // Get the state abbreviation. string = index(start, ' ')+1; nextSpace = index(string, ' '); *nextSpace = '\0'; strcpy(record->stateAbbr, string); // Get the state name. string = nextSpace+1; strcpy(record->stateName, string); break; case '3': // Get the nation abbreviation. string = index(start, ' ')+1; nextSpace = index(string, ' '); *nextSpace = '\0'; strcpy(record->nationAbbr, string); // Get the nation name. string = nextSpace+1; strcpy(record->nationName, string); break; case 'A': // Get the area code. string = index(start, ' ')+1; strcpy(record->areaCode, string); break; case 'E': // Get the elevation. string = index(start, ' ')+1; strcpy(record->elevation, string); break; case 'F': // Get the feature code. string = index(start, ' ')+1; nextSpace = index(string, ' '); *nextSpace = '\0'; strcpy(record->featureCode, string); // Get the feature name. string = nextSpace+1; strcpy(record->featureName, string); break; case 'L': // Get the latitude. string = index(start, ' ')+1; nextSpace = index(string, ' '); for(blank = 0; blank < 3; blank++) nextSpace = index(nextSpace+1, ' '); *nextSpace = '\0'; strcpy(record->latitude, string); // Get the longitude. string = nextSpace+1; strcpy(record->longitude, string); break; case 'P': // Get the census. string = index(start, ' ')+1; strcpy(record->census, string); break; case 'R': // Get the remark. string = index(start, ' ')+1; strcpy(record->remark, string); break; case 'T': // Get the time zone. string = index(start, ' ')+1; strcpy(record->timeZone, string); break; case 'Z': // Get the zip codes. string = index(start, ' ')+1; strcat(record->zip, string); break; } if(index(linefeed+1, '\n')) start = linefeed + 1; else done = YES; } free(copy); return stateFound; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.