static void processLine(const char *line, RILChannelCtx *p_channel) { ATResponse *p_response = p_channel->p_response; const char *smsPDU = p_channel->smsPDU; //Move out to the function //pthread_mutex_lock(&p_channel->commandmutex); int isIntermediateResult = 0; if (p_response == NULL) { /* no command pending */ handleUnsolicited(line, p_channel); return; } else { switch (p_channel->type) { case NO_RESULT: //handleUnsolicited(line,p_channel); break; case NUMERIC: if (p_response->p_intermediates == NULL && isdigit(line[0]) ) { addIntermediate(line, p_channel); isIntermediateResult = 1; } else { /* either we already have an intermediate response or * the line doesn't begin with a digit */ //handleUnsolicited(line,p_channel); } break; case SINGLELINE: if (p_response->p_intermediates == NULL && strStartsWith(line, p_channel->responsePrefix) ) { addIntermediate(line, p_channel); isIntermediateResult = 1; } else { /* we already have an intermediate response */ //handleUnsolicited(line,p_channel); } break; case MULTILINE: if (strStartsWith(line, p_channel->responsePrefix)) { addIntermediate(line, p_channel); isIntermediateResult = 1; } else { //handleUnsolicited(line,p_channel); } break; /* atci start */ case RAW: if (!isFinalResponseSuccess(line) && !isFinalResponseErrorEx(line, p_channel) && !isIntermediatePattern(line) ) { addIntermediate(line, p_channel); isIntermediateResult = 1; } break; /* atci end */ default: /* this should never be reached */ RLOGE("Unsupported AT command type %d\n", p_channel->type); //handleUnsolicited(line,p_channel); break; } } if (isIntermediateResult) { /* No need to run the following code*/ } else if (isFinalResponseSuccess(line)) { p_response->success = 1; handleFinalResponse(line, p_channel); } else if (isFinalResponseErrorEx(line, p_channel)) { p_response->success = 0; handleFinalResponse(line, p_channel); } else if (smsPDU != NULL && 0 == strcmp(line, "> ")) { // See eg. TS 27.005 4.3 // Commands like AT+CMGS have a "> " prompt writeCtrlZ(smsPDU, p_channel); smsPDU = NULL; } else if (isIntermediatePattern(line)) { p_response->success = 1; handleFinalResponse(line, p_channel); } else { handleUnsolicited(line, p_channel); } // Move out to the function //pthread_mutex_unlock(&p_channel->commandmutex); }
int main(int argc, char* argv[]) { // SDL variables SDL_Surface *screen, *dragonos, *background, *plane; SDL_Rect dragonosPosition, backgroundPosition, planePosition; char serialPortBuffer[SERIAL_PORT_BUFFER_LENGTH] = {0}; int serialPortReadDataLength = 0; // Connect to the Arduino board via the serial port with Baudrate = 9600 if(!Arduino_connect(SERIAL_PORT, 9600)) exit(EXIT_FAILURE); // Initialize the SDL lib if (SDL_Init(SDL_INIT_VIDEO) == -1) { fprintf(stderr, "SDL initialization error : %s\n", SDL_GetError()); exit(EXIT_FAILURE); } // Setup the the main window screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF); if(screen == NULL) { fprintf(stderr, "SDL screen load error : %s\n", SDL_GetError()); exit(EXIT_FAILURE); } SDL_WM_SetCaption("Communication between SDL and Arduino", NULL); SDL_EnableKeyRepeat(10, 10); // Load images dragonos = IMG_Load("data/Dragonos.png"); background = IMG_Load("data/Water.jpg"); plane = IMG_Load("data/Plane.png"); // Set the images initial positions dragonosPosition.x = 10; dragonosPosition.y = 10; backgroundPosition.x = 0; backgroundPosition.y = background->h - 600; backgroundPosition.h = 600; backgroundPosition.w = background->w; planePosition.x = screen->w/2 - plane->w/2; planePosition.y = screen->h - plane->h - 20; int quit = 0; SDL_Event event; while(!quit) { // Check for SDL events if(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: quit = 1; break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_ESCAPE: quit = 1; break; case SDLK_UP: case SDLK_DOWN: case SDLK_RIGHT: case SDLK_LEFT: // Move the plane move(screen, plane, &planePosition, event.key.keysym.sym); break; } break; } } // Read from Arduino via serial port serialPortReadDataLength = Arduino_read(serialPortBuffer, SERIAL_PORT_BUFFER_LENGTH); serialPortBuffer[serialPortReadDataLength] = 0; if(serialPortReadDataLength > 0) { if(strStartsWith("up", serialPortBuffer)) { move(screen, plane, &planePosition, SDLK_UP); } else if(strStartsWith("down", serialPortBuffer)) { move(screen, plane, &planePosition, SDLK_DOWN); } else if(strStartsWith("right", serialPortBuffer)) { move(screen, plane, &planePosition, SDLK_RIGHT); } else if(strStartsWith("left", serialPortBuffer)) { move(screen, plane, &planePosition, SDLK_LEFT); } } // Send Notifications to the Arduino via serial port if(planePosition.x == 0) Arduino_write("Border Left\n", 12); else if(planePosition.x == 800 - plane->w) Arduino_write("Border Right\n", 13); else if(planePosition.y == 0) Arduino_write("Border Top\n", 11); else if(planePosition.y == 600 - plane->h) Arduino_write("Border Bottom\n", 14); // Update the sea position backgroundPosition.y -= 3; if(backgroundPosition.y == 0) backgroundPosition.y = background->h - 600; // Update the screen SDL_BlitSurface(background, &backgroundPosition, screen, NULL); SDL_BlitSurface(plane, NULL, screen, &planePosition); SDL_BlitSurface(dragonos, NULL, screen, &dragonosPosition); SDL_Flip(screen); SDL_Delay(10); } SDL_FreeSurface(background); SDL_FreeSurface(dragonos); SDL_FreeSurface(plane); SDL_Quit(); // Close Arduino connection Arduino_disconnect(); return EXIT_SUCCESS; }
int at_send_command_to_data_channel(const char *command, ATResponse **pp_outResponse, RILChannelCtx *p_channel) { const char* line = NULL; int ret = writeline(command, p_channel); if (ret == 0) { p_channel->p_response = at_response_new(); do { line = readline(p_channel); if (line != NULL) RLOGI("readline: %s", line); else RLOGI("readline: EMPTY"); } while (line != NULL && !(strcmp(line, "OK") == 0 || strcmp(line, "NO CARRIER") == 0 || strStartsWith(line, "CONNECT") == 1 || strstr(line, "ERROR"))); if (line != NULL) { RLOGI("process line: %s", line); processLine(line, p_channel); if (pp_outResponse == NULL) { at_response_free(p_channel->p_response); } else { reverseIntermediates(p_channel->p_response); *pp_outResponse = p_channel->p_response; } return 0; } } return AT_ERROR_GENERIC; }
static void processLine(const char *line) { struct atcontext *context = get_at_context(); ENTER; pthread_mutex_lock(&context->commandmutex); if (context->response == NULL) { /* No command pending. */ handleUnsolicited(line); } else if (isFinalResponseSuccess(line)) { context->response->success = 1; handleFinalResponse(line); } else if (isFinalResponseError(line)) { context->response->success = 0; handleFinalResponse(line); } else if (context->smsPDU != NULL && 0 == strcmp(line, "> ")) { /* See eg. TS 27.005 4.3. Commands like AT+CMGS have a "> " prompt. */ writeTransparentMode(context->smsPDU); context->smsPDU = NULL; } else switch (context->type) { case NO_RESULT: handleUnsolicited(line); break; case NUMERIC: if (context->response->p_intermediates == NULL && isdigit(line[0]) ) { addIntermediate(line); } else { /* Either we already have an intermediate response or the line doesn't begin with a digit. */ handleUnsolicited(line); } break; case SINGLELINE: if (context->response->p_intermediates == NULL && strStartsWith (line, context->responsePrefix) ) { addIntermediate(line); } else { /* We already have an intermediate response. */ handleUnsolicited(line); } break; case MULTILINE: if (strStartsWith (line, context->responsePrefix)) { addIntermediate(line); } else { handleUnsolicited(line); } break; default: /* This should never be reached */ MBMLOGE("Unsupported AT command type %d\n", context->type); handleUnsolicited(line); break; } pthread_mutex_unlock(&context->commandmutex); EXIT; }
bool GameSettingsPresetInfo::fromString( const std::string & str) { bool global = GetBaseFilename(str) == str || strStartsWith(str, "./"); *this = settingsInfo(str, global); return true; }
bool dmtcp::Util::strStartsWith(const dmtcp::string& str, const char *pattern) { return strStartsWith(str.c_str(), pattern); }
static bool parseRsa(DxCore *dx, const char *path, void *dest) { bool ret = false; do { unzFile file = unzOpen(path); if (file == NULL) { DBG("unzOpen failed!\n"); break; } unz_global_info global_info; unz_file_info file_info; char *str_meta_inf = dx->sub_206C(s_meta_inf, sizeof(s_meta_inf)); char *str_cert_entry = dx->sub_206C(s_cert_entry, sizeof(s_cert_entry)); char entry[512]; char rsa_entry[512]; int n = -1; int r = unzGetGlobalInfo(file, &global_info); for (int i = 0; i < global_info.number_entry; i++) { if ((r = unzGetCurrentFileInfo(file, &file_info, entry, sizeof(entry), NULL, 0, NULL, 0)) != UNZ_OK) { DBG("unzGetCurrentFileInfo error\n"); break; } if (strStartsWith(entry, str_meta_inf) && strEndsWidth(entry, str_cert_entry) && 1 == charCountInStr(entry, '/')) { DBG("found entry <%s>\n", entry); if ((r = unzOpenCurrentFilePassword(file, NULL)) != UNZ_OK) { DBG("unzOpenCurrentFilePassword error\n"); break; } int left = sizeof(rsa_entry); int pos = 0; while ((n = unzReadCurrentFile(file, rsa_entry + pos, left)) > 0) { left -= n; pos += n; } DBG("read %d bytes\n", pos); ret = pos == sizeof(rsa_entry); unzCloseCurrentFile(file); break; } if (i < global_info.number_entry - 1) { if ((r = unzGoToNextFile(file)) != UNZ_OK) { DBG("unzGoToNextFile error\n"); break; } } } free(str_meta_inf); free(str_cert_entry); unzClose(file); if (ret == true) { dx->sub_12FC(rsa_entry, sizeof(rsa_entry), dest); } } while (0); DBG("parseRsa ret = %d\n", ret); return ret; }
ALERROR CMultiverseCatalogEntry::CreateFromJSON (const CJSONValue &Entry, CMultiverseCatalogEntry **retpEntry, CString *retsResult) // CreateFromJSON // // Creates a new entry from a JSON value. { int i; CMultiverseCatalogEntry *pNewEntry = new CMultiverseCatalogEntry; pNewEntry->m_sUNID = Entry.GetElement(FIELD_UNID).AsString(); if (pNewEntry->m_sUNID.IsBlank()) { delete pNewEntry; *retsResult = ERR_INVALID_UNID; return ERR_FAIL; } pNewEntry->m_dwRelease = (DWORD)Entry.GetElement(FIELD_RELEASE).AsInt32(); pNewEntry->m_dwVersion = (DWORD)Entry.GetElement(FIELD_VERSION).AsInt32(); pNewEntry->m_sName = Entry.GetElement(FIELD_NAME).AsString(); pNewEntry->m_sDesc = Entry.GetElement(FIELD_DESCRIPTION).AsString(); // Parse the fully qualified UNID and get just the hex UNID. if (strStartsWith(pNewEntry->m_sUNID, STR_UNID_PREFIX)) { pNewEntry->m_dwUNID = strParseIntOfBase(pNewEntry->m_sUNID.GetASCIIZPointer() + STR_UNID_PREFIX.GetLength(), 16, 0); if (pNewEntry->m_dwUNID == 0) { delete pNewEntry; *retsResult = ERR_INVALID_UNID; return ERR_FAIL; } } else { delete pNewEntry; *retsResult = ERR_INVALID_UNID; return ERR_FAIL; } // Get the type CString sType = Entry.GetElement(FIELD_TYPE).AsString(); if (strEquals(sType, TYPE_ADVENTURE)) pNewEntry->m_iType = extAdventure; else if (strEquals(sType, TYPE_LIBRARY)) pNewEntry->m_iType = extLibrary; else pNewEntry->m_iType = extExtension; // Get the license type CString sLicense = Entry.GetElement(FIELD_LICENSE_TYPE).AsString(); if (strEquals(sLicense, LICENSE_AUTO)) pNewEntry->m_iLicenseType = licenseAuto; else if (strEquals(sLicense, LICENSE_CORE)) pNewEntry->m_iLicenseType = licenseCore; else if (strEquals(sLicense, LICENSE_FREE)) pNewEntry->m_iLicenseType = licenseFree; else if (strEquals(sLicense, LICENSE_PAID)) pNewEntry->m_iLicenseType = licensePaid; else pNewEntry->m_iLicenseType = licenseUnknown; // Get the TDB file (only if not core) if (pNewEntry->m_iLicenseType != licenseCore) { if (pNewEntry->m_TDBFile.InitFromJSON(Entry.GetElement(FIELD_FILE_TDB), retsResult) != NOERROR) { delete pNewEntry; return ERR_FAIL; } } // Get the resources const CJSONValue &Resources = Entry.GetElement(FIELD_RESOURCES); if (Resources.GetType() == CJSONValue::typeArray) { pNewEntry->m_Resources.InsertEmpty(Resources.GetCount()); for (i = 0; i < Resources.GetCount(); i++) { if (pNewEntry->m_Resources[i].InitFromJSON(Resources.GetElement(i), retsResult) != NOERROR) { delete pNewEntry; return ERR_FAIL; } } } // Initialize status pNewEntry->m_iStatus = statusUnknown; pNewEntry->m_pIcon = NULL; // Done *retpEntry = pNewEntry; return NOERROR; }
bool CUserInfoSession::OnProcessMessage (const SArchonMessage &Msg) // OnProcessMessage // // We received a reply from Aeon { int i; // If this is an error, then we return the error back to the client if (IsError(Msg)) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, Msg.dPayload); return false; } // If we're waiting for the user record, then see if we can process it now. if (m_iState == stateWaitingForUserRecord) { // If we get back nil then the user does not exist. if (Msg.dPayload.IsNil()) { SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, strPattern(ERR_UNKNOWN_USERNAME, m_sUsername)); return false; } // Otherwise, we handle the result based on the original message else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_CHECK_PASSWORD_SHA1)) { // Get the parameters from the original message CDatum dChallenge = GetOriginalMsg().dPayload.GetElement(1); CDatum dResponse = GetOriginalMsg().dPayload.GetElement(2); // Get the password has from the response CDatum dAuthDesc = Msg.dPayload.GetElement(FIELD_AUTH_DESC); CDatum dPasswordHash = dAuthDesc.GetElement(FIELD_CREDENTIALS); // Create a response to the challenge based on the password hash that // we have stored. CDatum dCorrect = CAI1Protocol::CreateSHAPasswordChallengeResponse(dPasswordHash, dChallenge); // Compare the correct response to the actual if ((const CIPInteger &)dResponse == (const CIPInteger &)dCorrect) return UpdateLoginSuccess(stateWaitingForSuccessUpdate); else return UpdateLoginFailure(); } // Cryptosaur.getUser else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_GET_USER)) { CDatum dUserData = Msg.dPayload; if (dUserData.IsNil()) { SendMessageReply(MSG_REPLY_DATA, CDatum()); return false; } // Generate a sanitized user record CComplexStruct *pReply = new CComplexStruct; pReply->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME)); // Sanitize rights CDatum dRights = dUserData.GetElement(FIELD_RIGHTS); if (!m_sScope.IsEmpty()) { CComplexArray *pRights = new CComplexArray; for (i = 0; i < dRights.GetCount(); i++) if (strStartsWith(dRights.GetElement(i), m_sScope)) pRights->Insert(dRights.GetElement(i)); pReply->SetElement(FIELD_RIGHTS, CDatum(pRights)); } else pReply->SetElement(FIELD_RIGHTS, dRights); // Done SendMessageReply(MSG_REPLY_DATA, CDatum(pReply)); return false; } // Cryptosaur.hasRights else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_HAS_RIGHTS)) { CDatum dRights = Msg.dPayload.GetElement(FIELD_RIGHTS); CDatum dRightsRequired = m_dPayload.GetElement(1); // Get the rights from the user CAttributeList Rights; dRights.AsAttributeList(&Rights); // Check for (i = 0; i < dRightsRequired.GetCount(); i++) { if (!Rights.HasAttribute(dRightsRequired.GetElement(i))) { SendMessageReply(MSG_REPLY_DATA, CDatum()); return false; } } // We have all rights SendMessageReply(MSG_REPLY_DATA, CDatum(CDatum::constTrue)); return false; } // Cryptosaur.loginUser else if (strEquals(GetOriginalMsg().sMsg, MSG_CRYPTOSAUR_LOGIN_USER)) { // Get the parameters from the original message CDatum dRequestAuthDesc = GetOriginalMsg().dPayload.GetElement(1); CDatum dCredentials = dRequestAuthDesc.GetElement(FIELD_CREDENTIALS); CDatum dChallengeCredentials = dRequestAuthDesc.GetElement(FIELD_CHALLENGE_CREDENTIALS); CDatum dPassword = dRequestAuthDesc.GetElement(FIELD_PASSWORD); m_bActual = !dRequestAuthDesc.GetElement(FIELD_ACTUAL).IsNil(); if (!dRequestAuthDesc.GetElement(FIELD_AUTH_TOKEN_INFINITE).IsNil()) m_dwAuthTokenLifetime = 0; else { m_dwAuthTokenLifetime = (DWORD)(int)dRequestAuthDesc.GetElement(FIELD_AUTH_TOKEN_LIFETIME); if (m_dwAuthTokenLifetime == 0) m_dwAuthTokenLifetime = DEFAULT_AUTH_TOKEN_TIMEOUT; } // If we're not actual and have no scope, then we can't continue if (!m_bActual && m_sScope.IsEmpty()) { SendMessageReplyError(MSG_ERROR_UNABLE_TO_COMPLY, ERR_SCOPE_REQUIRED); return false; } // User data CDatum dUserData = Msg.dPayload; CDatum dAuthDesc; if (m_bActual) dAuthDesc = dUserData.GetElement(FIELD_AUTH_DESC); else dAuthDesc = dUserData.GetElement(strPattern("%s%s", m_sScope, FIELD_AUTH_DESC)); // If we have no authdesc, then we can't continue. This is likely // because the client is in a sandbox that the user has not registered // with. We treat it the same as a username/password failure. if (dAuthDesc.IsNil()) { SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD); return false; } // If we've failed more than 5 consecutive times, we may need to delay // the next login attempt. if ((int)dUserData.GetElement(FIELD_LOGIN_FAILURE_COUNT) > MAX_LOGIN_ATTEMPTS) { CDateTime LastLoginFailure = dUserData.GetElement(FIELD_LAST_LOGIN_FAILURE_ON); CTimeSpan TimeSinceLastFailure = timeSpan(LastLoginFailure, CDateTime(CDateTime::Now)); // If it has not been at least 1 hour, we return an error. if (TimeSinceLastFailure.Days() == 0 && TimeSinceLastFailure.Seconds() < LOGIN_TIMEOUT) { // Timeout SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_FAILURE_TIMEOUT); return false; } } // If we have straight credentials, then just compare bool bSuccess; if (!dCredentials.IsNil()) bSuccess = ((const CIPInteger &)dCredentials == (const CIPInteger &)dAuthDesc.GetElement(FIELD_CREDENTIALS)); // Otherwise, we compare against the challenge else if (!dChallengeCredentials.IsNil()) { // Get the challenge. If not provided then we get it from the user // record. CDatum dChallenge = GetOriginalMsg().dPayload.GetElement(2); if (dChallenge.IsNil()) { // Get the expiration time of the challenge const CDateTime &Expires = dAuthDesc.GetElement(FIELD_CHALLENGE_EXPIRATION); if (Expires < CDateTime(CDateTime::Now)) { SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD); return false; } dChallenge = dAuthDesc.GetElement(FIELD_CHALLENGE); } // Create a response to the challenge based on the password hash that // we have stored. CDatum dCorrectChallenge = CAI1Protocol::CreateSHAPasswordChallengeResponse( dAuthDesc.GetElement(FIELD_CREDENTIALS), dChallenge ); bSuccess = ((const CIPInteger &)dChallengeCredentials == (const CIPInteger &)dCorrectChallenge); } // Otherwise we expect a clear text password else if (!dPassword.IsNil()) { // We have to hash the password to compare with credentials. CIPInteger Credentials; CCryptosaurInterface::CreateCredentials(dUserData.GetElement(FIELD_USERNAME), dPassword, &Credentials); // Compare bSuccess = (Credentials == (const CIPInteger &)dAuthDesc.GetElement(FIELD_CREDENTIALS)); } else bSuccess = false; // Success or failure if (bSuccess) return UpdateLoginSuccess(stateWaitingForCredentials); else return UpdateLoginFailure(); } // Can never get here. else { ASSERT(false); return false; } } // Otherwise, if we're waiting for the user record update, then continue else if (m_iState == stateWaitingForSuccessUpdate) { // Since we succeeded, we send the user sanitized user record back. SendMessageReply(MSG_REPLY_DATA, CreateSanitizedUserRecord(Msg.dPayload)); return false; } // If we're waiting for credentials, compose them else if (m_iState == stateWaitingForCredentials) { // The mutation returns the full record CDatum dUserData = Msg.dPayload; // Compute the result CComplexStruct *pAuthToken = new CComplexStruct; pAuthToken->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME)); pAuthToken->SetElement(FIELD_RIGHTS, dUserData.GetElement(FIELD_RIGHTS)); if (!m_bActual) pAuthToken->SetElement(FIELD_SCOPE, m_sScope); CDatum dAuthToken = m_pEngine->GenerateAuthToken(CDatum(pAuthToken), m_dwAuthTokenLifetime); // Compose a basic user record CComplexStruct *pReply = new CComplexStruct; pReply->SetElement(FIELD_AUTH_TOKEN, dAuthToken); pReply->SetElement(FIELD_RIGHTS, dUserData.GetElement(FIELD_RIGHTS)); pReply->SetElement(FIELD_USERNAME, dUserData.GetElement(FIELD_USERNAME)); // Send the reply SendMessageReply(MSG_REPLY_DATA, CDatum(pReply)); // Done return false; } // Otherwise, failure else if (m_iState == stateWaitingForFailureUpdate) { CDatum dUserData = Msg.dPayload; // If we've exceeded our limit, log it int iAttempts = (int)dUserData.GetElement(FIELD_LOGIN_FAILURE_COUNT); if (iAttempts > MAX_LOGIN_ATTEMPTS) GetProcessCtx()->Log(MSG_LOG_INFO, strPattern(ERR_USERNAME_TIMEOUT, m_sUsername, iAttempts)); // Send a failure SendMessageReplyError(MSG_ERROR_DOES_NOT_EXIST, ERR_INVALID_USERNAME_OR_PASSWORD); return false; } // Can never get here else { ASSERT(false); return false; } }
void CCommandLineDisplay::AutoCompleteSearch (void) // AutocompleteSearch // // Searches the global symbol table for matches to the current command. { const CString sCurCmd = GetCurrentCmd(); CString sCommon; CString sHelp; ClearHint(); if (sCurCmd.IsBlank()) return; // Get the list of global symbols ICCItem *pGlobals = g_pUniverse->GetCC().GetGlobals(); int iMatches = 0; for (int i = 0; i < pGlobals->GetCount(); i++) { CString sGlobal = pGlobals->GetKey(i); // Partial match if (strStartsWith(sGlobal, sCurCmd)) { if (iMatches == 0) sCommon = sGlobal; // If we have multiple matching commands then find the longest common stem else { int iLen = min(sCommon.GetLength(), sGlobal.GetLength()); char *pPos1 = sCommon.GetPointer(); char *pPos2 = sGlobal.GetPointer(); int i; for (i = 0; i < iLen; i++) { if (CharLower((LPTSTR)(BYTE)(*pPos1)) != CharLower((LPTSTR)(BYTE)(*pPos2))) break; pPos1++; pPos2++; } sCommon.Truncate(i); m_sHint.Append(CONSTLIT(" ")); } // Append the command to the auto complete hint m_sHint.Append(sGlobal); iMatches++; } if (strEquals(sGlobal, sCurCmd)) { // Exact match - get help text ICCItem *pItem = pGlobals->GetElement(i); if (pItem->IsPrimitive()) sHelp = pItem->GetHelp(); } } // If the common stem is longer than the current command, then auto complete if (sCommon.GetLength() > sCurCmd.GetLength()) Input(strSubString(sCommon, sCurCmd.GetLength(), -1)); // If we only have one match then no need to show hint as we have either // auto completed or will show help text insead if (iMatches == 1) m_sHint = NULL_STR; if (!sHelp.IsBlank()) { if (!m_sHint.IsBlank()) m_sHint.Append(CONSTLIT("\n")); m_sHint.Append(sHelp); } }