--- liblscp/trunk/src/common.c 2004/07/07 23:41:07 187 +++ liblscp/trunk/src/common.c 2005/06/10 09:59:18 626 @@ -2,7 +2,7 @@ // /**************************************************************************** liblscp - LinuxSampler Control Protocol API - Copyright (C) 2004, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2004-2005, rncbc aka Rui Nuno Capela. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -49,14 +49,66 @@ pClient->pszResult = strdup(lscp_ltrim(pszResult)); } -// The main client requester call executive. -lscp_status_t lscp_client_call ( lscp_client_t *pClient, const char *pszQuery ) + +// The common client receiver executive. +lscp_status_t lscp_client_recv ( lscp_client_t *pClient, char *pchBuffer, int *pcchBuffer, int iTimeout ) { fd_set fds; // File descriptor list for select(). int fd, fdmax; // Maximum file descriptor number. struct timeval tv; // For specifying a timeout value. int iSelect; // Holds select return status. - int iTimeout; + + lscp_status_t ret = LSCP_FAILED; + + if (pClient == NULL) + return ret; + + // Prepare for waiting on select... + fd = (int) pClient->cmd.sock; + FD_ZERO(&fds); + FD_SET((unsigned int) fd, &fds); + fdmax = fd; + + // Use the timeout select feature... + if (iTimeout < 1) + iTimeout = pClient->iTimeout; + if (iTimeout >= 1000) { + tv.tv_sec = iTimeout / 1000; + iTimeout -= tv.tv_sec * 1000; + } + else tv.tv_sec = 0; + tv.tv_usec = iTimeout * 1000; + + // Wait for event... + iSelect = select(fdmax + 1, &fds, NULL, NULL, &tv); + if (iSelect > 0 && FD_ISSET(fd, &fds)) { + // May recv now... + *pcchBuffer = recv(pClient->cmd.sock, pchBuffer, *pcchBuffer, 0); + if (*pcchBuffer > 0) + ret = LSCP_OK; + else if (*pcchBuffer < 0) + lscp_socket_perror("lscp_client_recv: recv"); + else if (*pcchBuffer == 0) { + // Damn, server probably disconnected, + // we better free everything down here. + lscp_socket_agent_free(&(pClient->evt)); + lscp_socket_agent_free(&(pClient->cmd)); + // Fake a result message. + ret = LSCP_QUIT; + } + } // Check if select has timed out. + else if (iSelect == 0) + ret = LSCP_TIMEOUT; + else + lscp_socket_perror("lscp_client_recv: select"); + + return ret; +} + + +// The main client requester call executive. +lscp_status_t lscp_client_call ( lscp_client_t *pClient, const char *pszQuery ) +{ int cchQuery; char achResult[LSCP_BUFSIZ]; int cchResult; @@ -81,96 +133,97 @@ return ret; } + // Check if last transaction has timed out, in which case + // we'll retry wait and flush for some pending garbage... + if (pClient->iTimeoutCount > 0) { + cchResult = sizeof(achResult); + ret = lscp_client_recv(pClient, achResult, &cchResult, pClient->iTimeout); + if (ret == LSCP_OK) { + // We've got rid of timeout trouble (hopefully). + pClient->iTimeoutCount = 0; + } else { + // Things are worse than before. Fake a result message. + iErrno = (int) ret; + pszResult = "Failure during flush timeout operation"; + lscp_client_set_result(pClient, pszResult, iErrno); + return ret; + } + } + // Send data, and then, wait for the result... cchQuery = strlen(pszQuery); if (send(pClient->cmd.sock, pszQuery, cchQuery, 0) < cchQuery) { - lscp_socket_perror("_lscp_client_call: send"); + lscp_socket_perror("lscp_client_call: send"); pszResult = "Failure during send operation"; lscp_client_set_result(pClient, pszResult, iErrno); return ret; } - // Prepare for waiting on select... - fd = (int) pClient->cmd.sock; - FD_ZERO(&fds); - FD_SET((unsigned int) fd, &fds); - fdmax = fd; - - // Use the timeout select feature... - iTimeout = pClient->iTimeout; - if (iTimeout > 1000) { - tv.tv_sec = iTimeout / 1000; - iTimeout -= tv.tv_sec * 1000; - } - else tv.tv_sec = 0; - tv.tv_usec = iTimeout * 1000; - - // Wait for event... - iSelect = select(fdmax + 1, &fds, NULL, NULL, &tv); - if (iSelect > 0 && FD_ISSET(fd, &fds)) { - // May recv now... - cchResult = recv(pClient->cmd.sock, achResult, sizeof(achResult), 0); - if (cchResult > 0) { - // Assume early success. - ret = LSCP_OK; - // Always force the result to be null terminated (and trim trailing CRLFs)! - while (cchResult > 0 && (achResult[cchResult - 1] == '\n' || achResult[cchResult- 1] == '\r')) - cchResult--; - achResult[cchResult] = (char) 0; - // Check if the response it's an error or warning message. - if (strncasecmp(achResult, "WRN:", 4) == 0) - ret = LSCP_WARNING; - else if (strncasecmp(achResult, "ERR:", 4) == 0) - ret = LSCP_ERROR; - // So we got a result... - if (ret == LSCP_OK) { - // Reset errno in case of success. - iErrno = 0; - // Is it a special successful response? - if (strncasecmp(achResult, "OK[", 3) == 0) { - // Parse the OK message, get the return string under brackets... - pszToken = lscp_strtok(achResult, pszSeps, &(pch)); - if (pszToken) - pszResult = lscp_strtok(NULL, pszSeps, &(pch)); - } - else pszResult = achResult; - // The result string is now set to the command response, if any. - } else { - // Parse the error/warning message, skip first colon... + // Wait for receive event... + cchResult = sizeof(achResult); + ret = lscp_client_recv(pClient, achResult, &cchResult, pClient->iTimeout); + + switch (ret) { + + case LSCP_OK: + // Always force the result to be null terminated (and trim trailing CRLFs)! + while (cchResult > 0 && (achResult[cchResult - 1] == '\n' || achResult[cchResult- 1] == '\r')) + cchResult--; + achResult[cchResult] = (char) 0; + // Check if the response it's an error or warning message. + if (strncasecmp(achResult, "WRN:", 4) == 0) + ret = LSCP_WARNING; + else if (strncasecmp(achResult, "ERR:", 4) == 0) + ret = LSCP_ERROR; + // So we got a result... + if (ret == LSCP_OK) { + // Reset errno in case of success. + iErrno = 0; + // Is it a special successful response? + if (strncasecmp(achResult, "OK[", 3) == 0) { + // Parse the OK message, get the return string under brackets... pszToken = lscp_strtok(achResult, pszSeps, &(pch)); + if (pszToken) + pszResult = lscp_strtok(NULL, pszSeps, &(pch)); + } + else pszResult = achResult; + // The result string is now set to the command response, if any. + } else { + // Parse the error/warning message, skip first colon... + pszToken = lscp_strtok(achResult, pszSeps, &(pch)); + if (pszToken) { + // Get the error number... + pszToken = lscp_strtok(NULL, pszSeps, &(pch)); if (pszToken) { - // Get the error number... - pszToken = lscp_strtok(NULL, pszSeps, &(pch)); - if (pszToken) { - iErrno = atoi(pszToken); - // And make the message text our final result. - pszResult = lscp_strtok(NULL, pszSeps, &(pch)); - } + iErrno = atoi(pszToken); + // And make the message text our final result. + pszResult = lscp_strtok(NULL, pszSeps, &(pch)); } - // The result string is set to the error/warning message text. } + // The result string is set to the error/warning message text. } - else if (cchResult == 0) { - // Damn, server disconnected, we better free everything down here. - lscp_socket_agent_free(&(pClient->evt)); - lscp_socket_agent_free(&(pClient->cmd)); - // Fake a result message. - ret = LSCP_QUIT; - pszResult = "Server terminated the connection"; - iErrno = (int) ret; - } else { - // What's down? - lscp_socket_perror("_lscp_client_call: recv"); - pszResult = "Failure during receive operation"; - } - } // Check if select has timed out. - else if (iSelect == 0) { + break; + + case LSCP_TIMEOUT: + // We have trouble... + pClient->iTimeoutCount++; // Fake a result message. - ret = LSCP_TIMEOUT; pszResult = "Timeout during receive operation"; iErrno = (int) ret; - } - else lscp_socket_perror("_lscp_client_call: select"); + break; + + case LSCP_QUIT: + // Fake a result message. + pszResult = "Server terminated the connection"; + iErrno = (int) ret; + break; + + case LSCP_FAILED: + default: + // What's down? + pszResult = "Failure during receive operation"; + break; + } // Make the result official... lscp_client_set_result(pClient, pszResult, iErrno); @@ -295,9 +348,9 @@ --pch; *pch = (char) 0; // Make it official. - ppszSplit[i++] = lscp_unquote(&pszHead, 0); + ppszSplit[i] = lscp_unquote(&pszHead, 0); // Do we need to grow? - if (i >= iSize) { + if (++i >= iSize) { // Yes, but only grow in chunks. iSize += LSCP_SPLIT_CHUNK1; // Allocate and copy to new split array. @@ -362,7 +415,7 @@ pchHead = lscp_ltrim((char *) pszCsv); if (*pchHead == (char) 0) return NULL; - + // Initial size is one chunk away. iSize = LSCP_SPLIT_CHUNK1; // Allocate and split... @@ -383,9 +436,9 @@ // Pre-advance to next item. pchHead = pch + cchSeps; // Make it official. - piSplit[i++] = atoi(pchHead); + piSplit[i] = atoi(pchHead); // Do we need to grow? - if (i >= iSize) { + if (++i >= iSize) { // Yes, but only grow in chunks. iSize += LSCP_SPLIT_CHUNK1; // Allocate and copy to new split array. @@ -547,7 +600,7 @@ { lscp_param_t *pParams; int i; - + if (ppList) { if (*ppList) { pParams = *ppList; @@ -569,7 +622,7 @@ lscp_param_t *pNewParams; int iSize, iNewSize; int i = 0; - + if (ppList && *ppList) { pParams = *ppList; while (pParams[i].key) { @@ -588,8 +641,8 @@ iNewSize = iSize + LSCP_SPLIT_CHUNK1; pNewParams = (lscp_param_t *) malloc(iNewSize * sizeof(lscp_param_t)); for (i = 0; i < iSize; i++) { - pParams[i].key = pParams[i].key; - pParams[i].value = pParams[i].value; + pNewParams[i].key = pParams[i].key; + pNewParams[i].value = pParams[i].value; } for ( ; i < iNewSize; i++) { pNewParams[i].key = NULL; @@ -626,6 +679,30 @@ //------------------------------------------------------------------------- +// Server info struct helper functions. + +void lscp_server_info_init ( lscp_server_info_t *pServerInfo ) +{ + pServerInfo->description = NULL; + pServerInfo->version = NULL; +} + +void lscp_server_info_free ( lscp_server_info_t *pServerInfo ) +{ + if (pServerInfo->description) + free(pServerInfo->description); + if (pServerInfo->version) + free(pServerInfo->version); +} + +void lscp_server_info_reset ( lscp_server_info_t *pServerInfo ) +{ + lscp_server_info_free(pServerInfo); + lscp_server_info_init(pServerInfo); +} + + +//------------------------------------------------------------------------- // Engine info struct helper functions. void lscp_engine_info_init ( lscp_engine_info_t *pEngineInfo ) @@ -660,6 +737,7 @@ pChannelInfo->audio_routing = NULL; pChannelInfo->instrument_file = NULL; pChannelInfo->instrument_nr = 0; + pChannelInfo->instrument_name = NULL; pChannelInfo->instrument_status = 0; pChannelInfo->midi_device = 0; pChannelInfo->midi_port = 0; @@ -675,6 +753,8 @@ lscp_szsplit_destroy(pChannelInfo->audio_routing); if (pChannelInfo->instrument_file) free(pChannelInfo->instrument_file); + if (pChannelInfo->instrument_name) + free(pChannelInfo->instrument_name); } void lscp_channel_info_reset ( lscp_channel_info_t *pChannelInfo ) @@ -817,6 +897,7 @@ if (cchBuffer + 2 < cchMaxBuffer) { pszBuffer[cchBuffer++] = '\r'; pszBuffer[cchBuffer++] = '\n'; + pszBuffer[cchBuffer ] = (char) 0; } return cchBuffer;