/[svn]/liblscp/trunk/src/client.c
ViewVC logotype

Annotation of /liblscp/trunk/src/client.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 125 - (hide annotations) (download)
Mon Jun 14 21:04:04 2004 UTC (19 years, 9 months ago) by capela
File MIME type: text/plain
File size: 40589 byte(s)
* Added support for the new LIST commands (draft v.08).

1 capela 107 // client.c
2     //
3     /****************************************************************************
4     liblscp - LinuxSampler Control Protocol API
5     Copyright (C) 2004, rncbc aka Rui Nuno Capela. All rights reserved.
6    
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11    
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     Lesser General Public License for more details.
16    
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20    
21     *****************************************************************************/
22    
23     #include "common.h"
24    
25     // Default timeout value (in milliseconds).
26     #define LSCP_TIMEOUT_MSECS 500
27    
28    
29     // Local prototypes.
30    
31     static void _lscp_client_set_result (lscp_client_t *pClient, char *pszResult, int iErrno);
32     static void _lscp_client_udp_proc (void *pvClient);
33    
34    
35     //-------------------------------------------------------------------------
36     // Helper functions.
37    
38     // Result buffer internal settler.
39     static void _lscp_client_set_result ( lscp_client_t *pClient, char *pszResult, int iErrno )
40     {
41     if (pClient->pszResult)
42     free(pClient->pszResult);
43     pClient->pszResult = NULL;
44    
45     pClient->iErrno = iErrno;
46    
47     if (pszResult)
48     pClient->pszResult = strdup(lscp_ltrim(pszResult));
49     }
50    
51    
52     //-------------------------------------------------------------------------
53     // UDP service (datagram oriented).
54    
55     static void _lscp_client_udp_proc ( void *pvClient )
56     {
57     lscp_client_t *pClient = (lscp_client_t *) pvClient;
58     struct sockaddr_in addr;
59     int cAddr;
60     char achBuffer[LSCP_BUFSIZ];
61     int cchBuffer;
62     const char *pszSeps = " \r\n";
63     char *pszToken;
64     char *pch;
65    
66     #ifdef DEBUG
67     fprintf(stderr, "_lscp_client_udp_proc: Client waiting for events.\n");
68     #endif
69    
70     while (pClient->udp.iState) {
71     cAddr = sizeof(struct sockaddr_in);
72     cchBuffer = recvfrom(pClient->udp.sock, achBuffer, sizeof(achBuffer), 0, (struct sockaddr *) &addr, &cAddr);
73     if (cchBuffer > 0) {
74     #ifdef DEBUG
75     lscp_socket_trace("_lscp_client_udp_proc: recvfrom", &addr, achBuffer, cchBuffer);
76     #endif
77     if (strncasecmp(achBuffer, "PING ", 5) == 0) {
78     // Make sure received buffer it's null terminated.
79     achBuffer[cchBuffer] = (char) 0;
80     lscp_strtok(achBuffer, pszSeps, &(pch)); // Skip "PING"
81     lscp_strtok(NULL, pszSeps, &(pch)); // Skip port (must be the same as in addr)
82     pszToken = lscp_strtok(NULL, pszSeps, &(pch)); // Have session-id.
83     if (pszToken) {
84     // Set now client's session-id, if not already
85     if (pClient->sessid == NULL)
86     pClient->sessid = strdup(pszToken);
87     if (pClient->sessid && strcmp(pszToken, pClient->sessid) == 0) {
88     sprintf(achBuffer, "PONG %s\r\n", pClient->sessid);
89     cchBuffer = strlen(achBuffer);
90     if (sendto(pClient->udp.sock, achBuffer, cchBuffer, 0, (struct sockaddr *) &addr, cAddr) < cchBuffer)
91     lscp_socket_perror("_lscp_client_udp_proc: sendto");
92     #ifdef DEBUG
93     fprintf(stderr, "> %s", achBuffer);
94     #endif
95     }
96     }
97     // Done with life proof.
98     } else {
99     //
100     if ((*pClient->pfnCallback)(
101     pClient,
102     achBuffer,
103     cchBuffer,
104     pClient->pvData) != LSCP_OK) {
105     pClient->udp.iState = 0;
106     }
107     }
108     } else {
109     lscp_socket_perror("_lscp_client_udp_proc: recvfrom");
110     pClient->udp.iState = 0;
111     }
112     }
113    
114     #ifdef DEBUG
115     fprintf(stderr, "_lscp_client_udp_proc: Client closing.\n");
116     #endif
117     }
118    
119    
120     //-------------------------------------------------------------------------
121     // Client versioning teller fuunction.
122    
123    
124     /** Retrieve the current client library version string. */
125     const char* lscp_client_package (void) { return LSCP_PACKAGE; }
126    
127     /** Retrieve the current client library version string. */
128     const char* lscp_client_version (void) { return LSCP_VERSION; }
129    
130     /** Retrieve the current client library build timestamp string. */
131     const char* lscp_client_build (void) { return __DATE__ " " __TIME__; }
132    
133    
134     //-------------------------------------------------------------------------
135     // Client socket functions.
136    
137     /**
138     * Create a client instance, estabilishing a connection to a server hostname,
139     * which must be listening on the given port. A client callback function is
140     * also supplied for server notification event handling.
141     *
142     * @param pszHost Hostname of the linuxsampler listening server.
143     * @param iPort Port number of the linuxsampler listening server.
144     * @param pfnCallback Callback function to receive event notifications.
145     * @param pvData User context opaque data, that will be passed
146     * to the callback function.
147     *
148     * @returns The new client instance pointer if successfull, which shall be
149     * used on all subsequent client calls, NULL otherwise.
150     */
151     lscp_client_t* lscp_client_create ( const char *pszHost, int iPort, lscp_client_proc_t pfnCallback, void *pvData )
152     {
153     lscp_client_t *pClient;
154     struct hostent *pHost;
155     lscp_socket_t sock;
156     struct sockaddr_in addr;
157     int cAddr;
158     int iSockOpt = (-1);
159    
160     if (pfnCallback == NULL) {
161     fprintf(stderr, "lscp_client_create: Invalid client callback function.\n");
162     return NULL;
163     }
164    
165     pHost = gethostbyname(pszHost);
166     if (pHost == NULL) {
167 capela 114 lscp_socket_herror("lscp_client_create: gethostbyname");
168 capela 107 return NULL;
169     }
170    
171     // Allocate client descriptor...
172    
173     pClient = (lscp_client_t *) malloc(sizeof(lscp_client_t));
174     if (pClient == NULL) {
175     fprintf(stderr, "lscp_client_create: Out of memory.\n");
176     return NULL;
177     }
178     memset(pClient, 0, sizeof(lscp_client_t));
179    
180     pClient->pfnCallback = pfnCallback;
181     pClient->pvData = pvData;
182    
183     #ifdef DEBUG
184     fprintf(stderr, "lscp_client_create: pClient=%p: pszHost=%s iPort=%d.\n", pClient, pszHost, iPort);
185     #endif
186    
187     // Prepare the TCP connection socket...
188    
189     sock = socket(AF_INET, SOCK_STREAM, 0);
190     if (sock == INVALID_SOCKET) {
191     lscp_socket_perror("lscp_client_create: tcp: socket");
192     free(pClient);
193     return NULL;
194     }
195    
196     #if defined(WIN32)
197     if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR)
198     lscp_socket_perror("lscp_client_create: tcp: setsockopt(SO_DONTLINGER)");
199     #endif
200    
201     #ifdef DEBUG
202     lscp_socket_getopts("lscp_client_create: tcp", sock);
203     #endif
204    
205     cAddr = sizeof(struct sockaddr_in);
206     memset((char *) &addr, 0, cAddr);
207     addr.sin_family = pHost->h_addrtype;
208     memmove((char *) &(addr.sin_addr), pHost->h_addr, pHost->h_length);
209     addr.sin_port = htons((short) iPort);
210    
211     if (connect(sock, (struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) {
212     lscp_socket_perror("lscp_client_create: tcp: connect");
213     closesocket(sock);
214     free(pClient);
215     return NULL;
216     }
217    
218     lscp_socket_agent_init(&(pClient->tcp), sock, &addr, cAddr);
219    
220     #ifdef DEBUG
221     fprintf(stderr, "lscp_client_create: tcp: pClient=%p: sock=%d addr=%s port=%d.\n", pClient, pClient->tcp.sock, inet_ntoa(pClient->tcp.addr.sin_addr), ntohs(pClient->tcp.addr.sin_port));
222     #endif
223    
224     // Prepare the UDP datagram service socket...
225    
226     sock = socket(AF_INET, SOCK_DGRAM, 0);
227     if (sock == INVALID_SOCKET) {
228     lscp_socket_perror("lscp_client_create: udp: socket");
229     lscp_socket_agent_free(&(pClient->tcp));
230     free(pClient);
231     return NULL;
232     }
233    
234     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR)
235     lscp_socket_perror("lscp_client_create: udp: setsockopt(SO_REUSEADDR)");
236    
237     #ifdef DEBUG
238     lscp_socket_getopts("lscp_client_create: udp", sock);
239     #endif
240    
241     cAddr = sizeof(struct sockaddr_in);
242     memset((char *) &addr, 0, cAddr);
243     addr.sin_family = AF_INET;
244     addr.sin_addr.s_addr = htonl(INADDR_ANY);
245     addr.sin_port = htons(0);
246    
247     if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) {
248     lscp_socket_perror("lscp_client_create: udp: bind");
249     lscp_socket_agent_free(&(pClient->tcp));
250     closesocket(sock);
251     free(pClient);
252     return NULL;
253     }
254    
255     if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) {
256     lscp_socket_perror("lscp_client_create: udp: getsockname");
257     lscp_socket_agent_free(&(pClient->tcp));
258     closesocket(sock);
259     free(pClient);
260     return NULL;
261     }
262    
263     lscp_socket_agent_init(&(pClient->udp), sock, &addr, cAddr);
264    
265     #ifdef DEBUG
266     fprintf(stderr, "lscp_client_create: udp: pClient=%p: sock=%d addr=%s port=%d.\n", pClient, pClient->udp.sock, inet_ntoa(pClient->udp.addr.sin_addr), ntohs(pClient->udp.addr.sin_port));
267     #endif
268    
269     // No session id, yet.
270     pClient->sessid = NULL;
271     // Initialize cached members.
272     pClient->audio_drivers = NULL;
273     pClient->midi_drivers = NULL;
274 capela 125 pClient->audio_devices = NULL;
275     pClient->midi_devices = NULL;
276 capela 107 pClient->engines = NULL;
277 capela 125 pClient->channels = NULL;
278 capela 107 lscp_driver_info_init(&(pClient->audio_info));
279     lscp_driver_info_init(&(pClient->midi_info));
280     lscp_engine_info_init(&(pClient->engine_info));
281     lscp_channel_info_init(&(pClient->channel_info));
282     // Initialize error stuff.
283     pClient->pszResult = NULL;
284     pClient->iErrno = -1;
285     // Stream usage stuff.
286     pClient->buffer_fill = NULL;
287     pClient->iStreamCount = 0;
288     // Default timeout value.
289     pClient->iTimeout = LSCP_TIMEOUT_MSECS;
290    
291     // Initialize the transaction mutex.
292     lscp_mutex_init(pClient->mutex);
293    
294     // Now's finally time to startup threads...
295     // UDP service thread...
296     if (lscp_socket_agent_start(&(pClient->udp), _lscp_client_udp_proc, pClient, 0) != LSCP_OK) {
297     lscp_socket_agent_free(&(pClient->tcp));
298     lscp_socket_agent_free(&(pClient->udp));
299     lscp_mutex_destroy(pClient->mutex);
300     free(pClient);
301     return NULL;
302     }
303    
304     // Finally we've some success...
305     return pClient;
306     }
307    
308    
309     /**
310     * Wait for a client instance to terminate graciously.
311     *
312     * @param pClient Pointer to client instance structure.
313     */
314     lscp_status_t lscp_client_join ( lscp_client_t *pClient )
315     {
316     if (pClient == NULL)
317     return LSCP_FAILED;
318    
319     #ifdef DEBUG
320     fprintf(stderr, "lscp_client_join: pClient=%p.\n", pClient);
321     #endif
322    
323     // lscp_socket_agent_join(&(pClient->udp));
324     lscp_socket_agent_join(&(pClient->tcp));
325    
326     return LSCP_OK;
327     }
328    
329    
330     /**
331     * Terminate and destroy a client instance.
332     *
333     * @param pClient Pointer to client instance structure.
334     *
335     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
336     */
337     lscp_status_t lscp_client_destroy ( lscp_client_t *pClient )
338     {
339     if (pClient == NULL)
340     return LSCP_FAILED;
341    
342     #ifdef DEBUG
343     fprintf(stderr, "lscp_client_destroy: pClient=%p.\n", pClient);
344     #endif
345    
346     // Free session-id, if any.
347     if (pClient->sessid)
348     free(pClient->sessid);
349     pClient->sessid = NULL;
350     // Free up all cached members.
351     lscp_channel_info_reset(&(pClient->channel_info));
352     lscp_engine_info_reset(&(pClient->engine_info));
353     lscp_driver_info_reset(&(pClient->midi_info));
354     lscp_driver_info_reset(&(pClient->audio_info));
355     // Free available engine table.
356     lscp_szsplit_destroy(pClient->audio_drivers);
357     lscp_szsplit_destroy(pClient->midi_drivers);
358 capela 125 lscp_isplit_destroy(pClient->audio_devices);
359     lscp_isplit_destroy(pClient->midi_devices);
360 capela 107 lscp_szsplit_destroy(pClient->engines);
361 capela 125 lscp_isplit_destroy(pClient->channels);
362 capela 107 // Make them null.
363     pClient->audio_drivers = NULL;
364     pClient->midi_drivers = NULL;
365     pClient->engines = NULL;
366     // Free result error stuff.
367     _lscp_client_set_result(pClient, NULL, 0);
368     // Frre stream usage stuff.
369     if (pClient->buffer_fill)
370     free(pClient->buffer_fill);
371     pClient->buffer_fill = NULL;
372     pClient->iStreamCount = 0;
373     pClient->iTimeout = 0;
374    
375     // Free socket agents.
376     lscp_socket_agent_free(&(pClient->udp));
377     lscp_socket_agent_free(&(pClient->tcp));
378    
379     // Last but not least, free good ol'transaction mutex.
380     lscp_mutex_destroy(pClient->mutex);
381    
382     free(pClient);
383    
384     return LSCP_OK;
385     }
386    
387    
388     /**
389     * Set the client transaction timeout interval.
390     *
391     * @param pClient Pointer to client instance structure.
392     * @param iTimeout Transaction timeout in milliseconds.
393     *
394     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
395     */
396     lscp_status_t lscp_client_set_timeout ( lscp_client_t *pClient, int iTimeout )
397     {
398     if (pClient == NULL)
399     return LSCP_FAILED;
400     if (iTimeout < 0)
401     return LSCP_FAILED;
402    
403     pClient->iTimeout = iTimeout;
404     return LSCP_OK;
405     }
406    
407    
408     /**
409     * Get the client transaction timeout interval.
410     *
411     * @param pClient Pointer to client instance structure.
412     *
413     * @returns The current timeout value milliseconds, -1 in case of failure.
414     */
415     int lscp_client_get_timeout ( lscp_client_t *pClient )
416     {
417     if (pClient == NULL)
418     return -1;
419    
420     return pClient->iTimeout;
421     }
422    
423    
424     //-------------------------------------------------------------------------
425     // Client common protocol functions.
426    
427    
428     /**
429     * Submit a command query line string to the server. The query string
430     * must be cr/lf and null terminated. Besides the return code, the
431     * specific server response to the command request is made available
432     * by the @ref lscp_client_get_result and @ref lscp_client_get_errno
433     * function calls.
434     *
435     * @param pClient Pointer to client instance structure.
436     * @param pszQuery Command request line to be sent to server,
437     * must be cr/lf and null terminated.
438     *
439     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
440     */
441     lscp_status_t lscp_client_query ( lscp_client_t *pClient, const char *pszQuery )
442     {
443     fd_set fds; // File descriptor list for select().
444     int fd, fdmax; // Maximum file descriptor number.
445     struct timeval tv; // For specifying a timeout value.
446     int iSelect; // Holds select return status.
447     int iTimeout;
448     int cchQuery;
449     char achResult[LSCP_BUFSIZ];
450     int cchResult;
451     const char *pszSeps = ":[]";
452     char *pszResult;
453     char *pszToken;
454     char *pch;
455     int iErrno;
456    
457     lscp_status_t ret = LSCP_FAILED;
458    
459     if (pClient == NULL)
460     return ret;
461    
462     // Lock this section up.
463     lscp_mutex_lock(pClient->mutex);
464    
465     pszResult = NULL;
466     iErrno = -1;
467    
468     // Send data, and then, wait for the result...
469     cchQuery = strlen(pszQuery);
470     if (send(pClient->tcp.sock, pszQuery, cchQuery, 0) < cchQuery) {
471     lscp_socket_perror("lscp_client_query: send");
472 capela 125 pszResult = "Failure during send operation";
473     _lscp_client_set_result(pClient, pszResult, iErrno);
474 capela 107 lscp_mutex_unlock(pClient->mutex);
475     return ret;
476     }
477    
478     // Prepare for waiting on select...
479     fd = (int) pClient->tcp.sock;
480     FD_ZERO(&fds);
481     FD_SET((unsigned int) fd, &fds);
482     fdmax = fd;
483    
484     // Use the timeout select feature...
485     iTimeout = pClient->iTimeout;
486     if (iTimeout > 1000) {
487     tv.tv_sec = iTimeout / 1000;
488     iTimeout -= tv.tv_sec * 1000;
489     }
490     else tv.tv_sec = 0;
491     tv.tv_usec = iTimeout * 1000;
492    
493     // Wait for event...
494     iSelect = select(fdmax + 1, &fds, NULL, NULL, &tv);
495     if (iSelect > 0 && FD_ISSET(fd, &fds)) {
496     // May recv now...
497     cchResult = recv(pClient->tcp.sock, achResult, sizeof(achResult), 0);
498     if (cchResult > 0) {
499     // Assume early success.
500     ret = LSCP_OK;
501     // Always force the result to be null terminated (and trim trailing CRLFs)!
502     while (cchResult > 0 && (achResult[cchResult - 1] == '\n' || achResult[cchResult- 1] == '\r'))
503     cchResult--;
504     achResult[cchResult] = (char) 0;
505     // Check if the response it's an error or warning message.
506     if (strncasecmp(achResult, "WRN:", 4) == 0)
507     ret = LSCP_WARNING;
508     else if (strncasecmp(achResult, "ERR:", 4) == 0)
509     ret = LSCP_ERROR;
510     // So we got a result...
511     if (ret == LSCP_OK) {
512     // Reset errno in case of success.
513     iErrno = 0;
514     // Is it a special successful response?
515     if (strncasecmp(achResult, "OK[", 3) == 0) {
516     // Parse the OK message, get the return string under brackets...
517     pszToken = lscp_strtok(achResult, pszSeps, &(pch));
518     if (pszToken)
519     pszResult = lscp_strtok(NULL, pszSeps, &(pch));
520     }
521     else pszResult = achResult;
522     // The result string is now set to the command response, if any.
523     } else {
524     // Parse the error/warning message, skip first colon...
525     pszToken = lscp_strtok(achResult, pszSeps, &(pch));
526     if (pszToken) {
527     // Get the error number...
528     pszToken = lscp_strtok(NULL, pszSeps, &(pch));
529     if (pszToken) {
530     iErrno = atoi(pszToken);
531     // And make the message text our final result.
532     pszResult = lscp_strtok(NULL, pszSeps, &(pch));
533     }
534     }
535     // The result string is set to the error/warning message text.
536     }
537     }
538     else if (cchResult == 0) {
539     // Fake a result message.
540 capela 125 ret = LSCP_QUIT;
541 capela 107 pszResult = "Server terminated the connection";
542 capela 125 iErrno = (int) ret;
543     } else {
544     // What's down?
545     lscp_socket_perror("lscp_client_query: recv");
546     pszResult = "Failure during receive operation";
547 capela 107 }
548     } // Check if select has timed out.
549     else if (iSelect == 0) {
550     // Fake a result message.
551 capela 125 ret = LSCP_TIMEOUT;
552 capela 107 pszResult = "Timeout during receive operation";
553 capela 125 iErrno = (int) ret;
554     }
555 capela 107 else lscp_socket_perror("lscp_client_query: select");
556    
557     // Make the result official...
558     _lscp_client_set_result(pClient, pszResult, iErrno);
559    
560     // Can go on with it...
561     lscp_mutex_unlock(pClient->mutex);
562    
563     return ret;
564     }
565    
566    
567     /**
568     * Get the last received result string. In case of error or warning,
569     * this is the text of the error or warning message issued.
570     *
571     * @param pClient Pointer to client instance structure.
572     *
573     * @returns A pointer to the literal null-terminated result string as
574     * of the last command request.
575     */
576     const char *lscp_client_get_result ( lscp_client_t *pClient )
577     {
578     if (pClient == NULL)
579     return NULL;
580    
581     return pClient->pszResult;
582     }
583    
584    
585     /**
586     * Get the last error/warning number received.
587     *
588     * @param pClient Pointer to client instance structure.
589     *
590     * @returns The numerical value of the last error or warning
591     * response code received.
592     */
593     int lscp_client_get_errno ( lscp_client_t *pClient )
594     {
595     if (pClient == NULL)
596     return -1;
597    
598     return pClient->iErrno;
599     }
600    
601    
602     //-------------------------------------------------------------------------
603     // Client registration protocol functions.
604    
605     /**
606     * Register frontend for receiving UDP event messages:
607     * SUBSCRIBE NOTIFICATION <udp-port>
608     *
609     * @param pClient Pointer to client instance structure.
610     *
611     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
612     */
613     lscp_status_t lscp_client_subscribe ( lscp_client_t *pClient )
614     {
615     lscp_status_t ret;
616     char szQuery[LSCP_BUFSIZ];
617     const char *pszResult;
618     const char *pszSeps = "[]";
619     char *pszToken;
620     char *pch;
621    
622     if (pClient == NULL || pClient->sessid)
623     return LSCP_FAILED;
624    
625     sprintf(szQuery, "SUBSCRIBE NOTIFICATION %d\r\n", ntohs(pClient->udp.addr.sin_port));
626     ret = lscp_client_query(pClient, szQuery);
627     if (ret == LSCP_OK) {
628     pszResult = lscp_client_get_result(pClient);
629     #ifdef DEBUG
630     fprintf(stderr, "lscp_client_subscribe: %s\n", pszResult);
631     #endif
632     // Check for the session-id on "OK[sessid]" response.
633 capela 114 pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch));
634 capela 107 if (pszToken && strcasecmp(pszToken, "OK") == 0) {
635     pszToken = lscp_strtok(NULL, pszSeps, &(pch));
636     if (pszToken)
637     pClient->sessid = strdup(pszToken);
638     }
639     }
640    
641     return ret;
642     }
643    
644    
645     /**
646     * Deregister frontend for not receiving UDP event messages anymore:
647     * UNSUBSCRIBE NOTIFICATION <session-id>
648     *
649     * @param pClient Pointer to client instance structure.
650     *
651     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
652     */
653     lscp_status_t lscp_client_unsubscribe ( lscp_client_t *pClient )
654     {
655     lscp_status_t ret;
656     char szQuery[LSCP_BUFSIZ];
657    
658     if (pClient == NULL)
659     return LSCP_FAILED;
660     if (pClient->sessid == NULL)
661     return LSCP_FAILED;
662    
663     sprintf(szQuery, "UNSUBSCRIBE NOTIFICATION %s\n\n", pClient->sessid);
664     ret = lscp_client_query(pClient, szQuery);
665     if (ret == LSCP_OK) {
666     #ifdef DEBUG
667     fprintf(stderr, "lscp_client_unsubscribe: %s\n", lscp_client_get_result(pClient));
668     #endif
669     // Bail out session-id string.
670     free(pClient->sessid);
671     pClient->sessid = NULL;
672     }
673    
674     return ret;
675     }
676    
677    
678     //-------------------------------------------------------------------------
679     // Client command protocol functions.
680    
681     /**
682     * Loading an instrument:
683     * LOAD INSTRUMENT <filename> <instr-index> <sampler-channel>
684     *
685     * @param pClient Pointer to client instance structure.
686     * @param pszFileName Instrument file name.
687     * @param iInstrIndex Instrument index number.
688     * @param iSamplerChannel Sampler Channel.
689     *
690     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
691     */
692     lscp_status_t lscp_load_instrument ( lscp_client_t *pClient, const char *pszFileName, int iInstrIndex, int iSamplerChannel )
693     {
694     char szQuery[LSCP_BUFSIZ];
695    
696     if (pszFileName == NULL || iSamplerChannel < 0)
697     return LSCP_FAILED;
698    
699     sprintf(szQuery, "LOAD INSTRUMENT %s %d %d\r\n", pszFileName, iInstrIndex, iSamplerChannel);
700     return lscp_client_query(pClient, szQuery);
701     }
702    
703    
704     /**
705     * Loading a sampler engine:
706     * LOAD ENGINE <engine-name> <sampler-channel>
707     *
708     * @param pClient Pointer to client instance structure.
709     * @param pszEngineName Engine name.
710     * @param iSamplerChannel Sampler channel number.
711     *
712     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
713     */
714     lscp_status_t lscp_load_engine ( lscp_client_t *pClient, const char *pszEngineName, int iSamplerChannel )
715     {
716     char szQuery[LSCP_BUFSIZ];
717    
718     if (pszEngineName == NULL || iSamplerChannel < 0)
719     return LSCP_FAILED;
720    
721     sprintf(szQuery, "LOAD ENGINE %s %d\r\n", pszEngineName, iSamplerChannel);
722     return lscp_client_query(pClient, szQuery);
723     }
724    
725    
726     /**
727     * Current number of sampler channels:
728     * GET CHANNELS
729     *
730     * @param pClient Pointer to client instance structure.
731     *
732     * @returns The current total number of sampler channels on success,
733     * -1 otherwise.
734     */
735     int lscp_get_channels ( lscp_client_t *pClient )
736     {
737     int iChannels = -1;
738     if (lscp_client_query(pClient, "GET CHANNELS\r\n") == LSCP_OK)
739     iChannels = atoi(lscp_client_get_result(pClient));
740     return iChannels;
741     }
742    
743    
744     /**
745 capela 125 * List current sampler channels number identifiers:
746     * LIST CHANNELS
747     *
748     * @param pClient Pointer to client instance structure.
749     *
750     * @returns An array of the sampler channels identifiers as positive integers,
751     * terminated with -1 on success, NULL otherwise.
752     */
753     int *lscp_list_channels ( lscp_client_t *pClient )
754     {
755     const char *pszSeps = ",";
756    
757     if (pClient == NULL)
758     return NULL;
759    
760     if (pClient->channels) {
761     lscp_isplit_destroy(pClient->channels);
762     pClient->channels = NULL;
763     }
764    
765     if (lscp_client_query(pClient, "LIST CHANNELS\r\n") == LSCP_OK)
766     pClient->channels = lscp_isplit_create(lscp_client_get_result(pClient), pszSeps);
767    
768     return pClient->channels;
769     }
770    
771    
772     /**
773 capela 107 * Adding a new sampler channel:
774     * ADD CHANNEL
775     *
776     * @param pClient Pointer to client instance structure.
777     *
778     * @returns The new sampler channel number identifier,
779     * or -1 in case of failure.
780     */
781     int lscp_add_channel ( lscp_client_t *pClient )
782     {
783     int iSamplerChannel = -1;
784     if (lscp_client_query(pClient, "ADD CHANNEL\r\n") == LSCP_OK)
785     iSamplerChannel = atoi(lscp_client_get_result(pClient));
786     return iSamplerChannel;
787     }
788    
789    
790     /**
791     * Removing a sampler channel:
792     * REMOVE CHANNEL <sampler-channel>
793     *
794     * @param pClient Pointer to client instance structure.
795     * @param iSamplerChannel Sampler channel number.
796     *
797     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
798     */
799     lscp_status_t lscp_remove_channel ( lscp_client_t *pClient, int iSamplerChannel )
800     {
801     char szQuery[LSCP_BUFSIZ];
802    
803     if (iSamplerChannel < 0)
804     return LSCP_FAILED;
805    
806     sprintf(szQuery, "REMOVE CHANNEL %d\r\n", iSamplerChannel);
807     return lscp_client_query(pClient, szQuery);
808     }
809    
810    
811     /**
812     * Getting all available engines:
813     * GET AVAILABLE_ENGINES
814     *
815     * @param pClient Pointer to client instance structure.
816     *
817     * @returns A NULL terminated array of engine name strings,
818     * or NULL in case of failure.
819     */
820     const char **lscp_get_available_engines ( lscp_client_t *pClient )
821     {
822     const char *pszSeps = ",";
823    
824     if (pClient->engines) {
825     lscp_szsplit_destroy(pClient->engines);
826     pClient->engines = NULL;
827     }
828    
829     if (lscp_client_query(pClient, "GET AVAILABLE_ENGINES\r\n") == LSCP_OK)
830     pClient->engines = lscp_szsplit_create(lscp_client_get_result(pClient), pszSeps);
831    
832     return (const char **) pClient->engines;
833     }
834    
835    
836     /**
837     * Getting information about an engine.
838     * GET ENGINE INFO <engine-name>
839     *
840     * @param pClient Pointer to client instance structure.
841     * @param pszEngineName Engine name.
842     *
843     * @returns A pointer to a @ref lscp_engine_info_t structure, with all the
844     * information of the given sampler engine, or NULL in case of failure.
845     */
846     lscp_engine_info_t *lscp_get_engine_info ( lscp_client_t *pClient, const char *pszEngineName )
847     {
848     lscp_engine_info_t *pEngineInfo;
849     char szQuery[LSCP_BUFSIZ];
850     const char *pszResult;
851     const char *pszSeps = ":";
852     const char *pszCrlf = "\r\n";
853     char *pszToken;
854     char *pch;
855    
856     if (pszEngineName == NULL)
857     return NULL;
858    
859     pEngineInfo = &(pClient->engine_info);
860     lscp_engine_info_reset(pEngineInfo);
861    
862     sprintf(szQuery, "GET ENGINE INFO %s\r\n", pszEngineName);
863     if (lscp_client_query(pClient, szQuery) != LSCP_OK)
864     return NULL;
865    
866     pszResult = lscp_client_get_result(pClient);
867 capela 114 pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch));
868 capela 107 while (pszToken) {
869     if (strcasecmp(pszToken, "DESCRIPTION") == 0) {
870     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
871     if (pszToken)
872     pEngineInfo->description = lscp_unquote(&pszToken, 1);
873     }
874     else if (strcasecmp(pszToken, "VERSION") == 0) {
875     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
876     if (pszToken)
877     pEngineInfo->version = lscp_unquote(&pszToken, 1);
878     }
879     pszToken = lscp_strtok(NULL, pszSeps, &(pch));
880     }
881    
882     return pEngineInfo;
883     }
884    
885    
886     /**
887     * Getting sampler channel informations:
888     * GET CHANNEL INFO <sampler-channel>
889     *
890     * @param pClient Pointer to client instance structure.
891     * @param iSamplerChannel Sampler channel number.
892     *
893     * @returns A pointer to a @ref lscp_channel_info_t structure, with all the
894     * information of the given sampler channel, or NULL in case of failure.
895     */
896     lscp_channel_info_t *lscp_get_channel_info ( lscp_client_t *pClient, int iSamplerChannel )
897     {
898     lscp_channel_info_t *pChannelInfo;
899     char szQuery[LSCP_BUFSIZ];
900     const char *pszResult;
901     const char *pszSeps = ":";
902     const char *pszCrlf = "\r\n";
903     char *pszToken;
904     char *pch;
905    
906     if (iSamplerChannel < 0)
907     return NULL;
908    
909     pChannelInfo = &(pClient->channel_info);
910     lscp_channel_info_reset(pChannelInfo);
911    
912     sprintf(szQuery, "GET CHANNEL INFO %d\r\n", iSamplerChannel);
913     if (lscp_client_query(pClient, szQuery) != LSCP_OK)
914     return NULL;
915    
916     pszResult = lscp_client_get_result(pClient);
917 capela 114 pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch));
918 capela 107 while (pszToken) {
919     if (strcasecmp(pszToken, "ENGINE_NAME") == 0) {
920     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
921     if (pszToken)
922     pChannelInfo->engine_name = lscp_unquote(&pszToken, 1);
923     }
924     else if (strcasecmp(pszToken, "AUDIO_OUTPUT_DEVICE") == 0) {
925     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
926     if (pszToken)
927     pChannelInfo->audio_device = atoi(lscp_ltrim(pszToken));
928     }
929     else if (strcasecmp(pszToken, "AUDIO_OUTPUT_CHANNELS") == 0) {
930     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
931     if (pszToken)
932     pChannelInfo->audio_channels = atoi(lscp_ltrim(pszToken));
933     }
934     else if (strcasecmp(pszToken, "AUDIO_OUTPUT_ROUTING") == 0) {
935     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
936     if (pszToken)
937     pChannelInfo->audio_routing = lscp_szsplit_create(pszToken, ",");
938     }
939     else if (strcasecmp(pszToken, "INSTRUMENT_FILE") == 0) {
940     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
941     if (pszToken)
942     pChannelInfo->instrument_file = lscp_unquote(&pszToken, 1);
943     }
944     else if (strcasecmp(pszToken, "INSTRUMENT_NR") == 0) {
945     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
946     if (pszToken)
947     pChannelInfo->instrument_nr = atoi(lscp_ltrim(pszToken));
948     }
949     else if (strcasecmp(pszToken, "MIDI_INPUT_DEVICE") == 0) {
950     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
951     if (pszToken)
952     pChannelInfo->midi_device = atoi(lscp_ltrim(pszToken));
953     }
954     else if (strcasecmp(pszToken, "MIDI_INPUT_PORT") == 0) {
955     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
956     if (pszToken)
957     pChannelInfo->midi_port = atoi(lscp_ltrim(pszToken));
958     }
959     else if (strcasecmp(pszToken, "MIDI_INPUT_CHANNEL") == 0) {
960     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
961     if (pszToken)
962     pChannelInfo->midi_channel = atoi(lscp_ltrim(pszToken));
963     }
964     else if (strcasecmp(pszToken, "VOLUME") == 0) {
965     pszToken = lscp_strtok(NULL, pszCrlf, &(pch));
966     if (pszToken)
967     pChannelInfo->volume = (float) atof(lscp_ltrim(pszToken));
968     }
969     pszToken = lscp_strtok(NULL, pszSeps, &(pch));
970     }
971    
972     return pChannelInfo;
973     }
974    
975    
976     /**
977     * Current number of active voices:
978     * GET CHANNEL VOICE_COUNT <sampler-channel>
979     *
980     * @param pClient Pointer to client instance structure.
981     * @param iSamplerChannel Sampler channel number.
982     *
983     * @returns The number of voices currently active, -1 in case of failure.
984     */
985     int lscp_get_channel_voice_count ( lscp_client_t *pClient, int iSamplerChannel )
986     {
987     char szQuery[LSCP_BUFSIZ];
988     int iVoiceCount = -1;
989    
990     if (iSamplerChannel < 0)
991     return iVoiceCount;
992    
993     sprintf(szQuery, "GET CHANNEL VOICE_COUNT %d\r\n", iSamplerChannel);
994     if (lscp_client_query(pClient, szQuery) == LSCP_OK)
995     iVoiceCount = atoi(lscp_client_get_result(pClient));
996    
997     return iVoiceCount;
998     }
999    
1000    
1001     /**
1002     * Current number of active disk streams:
1003     * GET CHANNEL STREAM_COUNT <sampler-channel>
1004     *
1005     * @returns The number of active disk streams on success, -1 otherwise.
1006     */
1007     int lscp_get_channel_stream_count ( lscp_client_t *pClient, int iSamplerChannel )
1008     {
1009     char szQuery[LSCP_BUFSIZ];
1010     int iStreamCount = -1;
1011    
1012     if (iSamplerChannel < 0)
1013     return iStreamCount;
1014    
1015     sprintf(szQuery, "GET CHANNEL STREAM_COUNT %d\r\n", iSamplerChannel);
1016     if (lscp_client_query(pClient, szQuery) == LSCP_OK)
1017     iStreamCount = atoi(lscp_client_get_result(pClient));
1018    
1019     return iStreamCount;
1020     }
1021    
1022    
1023     /**
1024     * Current fill state of disk stream buffers:
1025     * GET CHANNEL BUFFER_FILL {BYTES|PERCENTAGE} <sampler-channel>
1026     *
1027     * @param pClient Pointer to client instance structure.
1028     * @param usage_type Usage type to be returned, either
1029     * @ref LSCP_USAGE_BYTES, or
1030     * @ref LSCP_USAGE_PERCENTAGE.
1031     * @param iSamplerChannel Sampler channel number.
1032     *
1033     * @returns A pointer to a @ref lscp_buffer_fill_t structure, with the
1034     * information of the current disk stream buffer fill usage, for the given
1035     * sampler channel, or NULL in case of failure.
1036     */
1037     lscp_buffer_fill_t *lscp_get_channel_buffer_fill ( lscp_client_t *pClient, lscp_usage_t usage_type, int iSamplerChannel )
1038     {
1039     lscp_buffer_fill_t *pBufferFill;
1040     char szQuery[LSCP_BUFSIZ];
1041     int iStreamCount;
1042     const char *pszUsageType = (usage_type == LSCP_USAGE_BYTES ? "BYTES" : "PERCENTAGE");
1043     const char *pszResult;
1044     const char *pszSeps = "[]%,";
1045     char *pszToken;
1046     char *pch;
1047     int iStream;
1048    
1049     iStreamCount = lscp_get_channel_stream_count(pClient, iSamplerChannel);
1050     if (pClient->iStreamCount != iStreamCount) {
1051     if (pClient->buffer_fill)
1052     free(pClient->buffer_fill);
1053     if (iStreamCount > 0)
1054     pClient->buffer_fill = (lscp_buffer_fill_t *) malloc(iStreamCount * sizeof(lscp_buffer_fill_t));
1055     else
1056     pClient->buffer_fill = NULL;
1057     pClient->iStreamCount = iStreamCount;
1058     }
1059    
1060     if (pClient->iStreamCount < 1)
1061     return NULL;
1062    
1063     iStream = 0;
1064     pBufferFill = pClient->buffer_fill;
1065    
1066     // Get buffer fill usage...
1067     sprintf(szQuery, "GET CHANNEL BUFFER_FILL %s %d\r\n", pszUsageType, iSamplerChannel);
1068     if (lscp_client_query(pClient, szQuery) == LSCP_OK) {
1069     pszResult = lscp_client_get_result(pClient);
1070 capela 114 pszToken = lscp_strtok((char *) pszResult, pszSeps, &(pch));
1071 capela 107 while (pszToken && iStream < pClient->iStreamCount) {
1072     if (*pszToken) {
1073     pBufferFill[iStream].stream_id = atol(pszToken);
1074     pszToken = lscp_strtok(NULL, pszSeps, &(pch));
1075     if (pszToken == NULL)
1076     break;
1077     pBufferFill[iStream].stream_usage = atol(pszToken);
1078     iStream++;
1079     }
1080     pszToken = lscp_strtok(NULL, pszSeps, &(pch));
1081     }
1082     } // Reset the usage, whatever it was before.
1083     else while (iStream < pClient->iStreamCount)
1084     pBufferFill[iStream++].stream_usage = 0;
1085    
1086     return pBufferFill;
1087     }
1088    
1089    
1090     /**
1091     * Setting audio output type:
1092     * SET CHANNEL AUDIO_OUTPUT_TYPE <sampler-channel> <audio-output-type>
1093     *
1094     * @param pClient Pointer to client instance structure.
1095     * @param iSamplerChannel Sampler channel number.
1096     * @param pszAudioDriver Audio output driver type (e.g. "ALSA" or "JACK").
1097     */
1098     lscp_status_t lscp_set_channel_audio_type ( lscp_client_t *pClient, int iSamplerChannel, const char *pszAudioDriver )
1099     {
1100     char szQuery[LSCP_BUFSIZ];
1101    
1102     if (iSamplerChannel < 0 || pszAudioDriver == NULL)
1103     return LSCP_FAILED;
1104    
1105     sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_TYPE %d %s\r\n", iSamplerChannel, pszAudioDriver);
1106     return lscp_client_query(pClient, szQuery);
1107     }
1108    
1109    
1110     /**
1111     * Setting audio output channel:
1112     * SET CHANNEL AUDIO_OUTPUT_CHANNEL <sampler-channel> <audio-output-chan> <audio-input-chan>
1113     *
1114     * @param pClient Pointer to client instance structure.
1115     * @param iSamplerChannel Sampler channel number.
1116     * @param iAudioOut Audio output device channel to be routed from.
1117     * @param iAudioIn Audio output device channel to be routed into.
1118     *
1119     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
1120     */
1121     lscp_status_t lscp_set_channel_audio_channel ( lscp_client_t *pClient, int iSamplerChannel, int iAudioOut, int iAudioIn )
1122     {
1123     char szQuery[LSCP_BUFSIZ];
1124    
1125     if (iSamplerChannel < 0 || iAudioOut < 0 || iAudioIn < 0)
1126     return LSCP_FAILED;
1127    
1128     sprintf(szQuery, "SET CHANNEL AUDIO_OUTPUT_CHANNELS %d %d %d\r\n", iSamplerChannel, iAudioOut, iAudioIn);
1129     return lscp_client_query(pClient, szQuery);
1130     }
1131    
1132    
1133     /**
1134     * Setting MIDI input type:
1135     * SET CHANNEL MIDI_INPUT_TYPE <sampler-channel> <midi-input-type>
1136     *
1137     * @param pClient Pointer to client instance structure.
1138     * @param iSamplerChannel Sampler channel number.
1139     * @param pszMidiDriver MIDI input driver type (e.g. "ALSA").
1140     *
1141     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
1142     */
1143     lscp_status_t lscp_set_channel_midi_type ( lscp_client_t *pClient, int iSamplerChannel, const char *pszMidiDriver )
1144     {
1145     char szQuery[LSCP_BUFSIZ];
1146    
1147     if (iSamplerChannel < 0 || pszMidiDriver == NULL)
1148     return LSCP_FAILED;
1149    
1150     sprintf(szQuery, "SET CHANNEL MIDI_INPUT_TYPE %d %s\r\n", iSamplerChannel, pszMidiDriver);
1151     return lscp_client_query(pClient, szQuery);
1152     }
1153    
1154    
1155     /**
1156     * Setting MIDI input port:
1157     * SET CHANNEL MIDI_INPUT_PORT <sampler-channel> <midi-input-port>
1158     *
1159     * @param pClient Pointer to client instance structure.
1160     * @param iSamplerChannel Sampler channel number.
1161     * @param iMidiPort MIDI input driver virtual port number.
1162     *
1163     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
1164     */
1165     lscp_status_t lscp_set_channel_midi_port ( lscp_client_t *pClient, int iSamplerChannel, int iMidiPort )
1166     {
1167     char szQuery[LSCP_BUFSIZ];
1168    
1169     if (iSamplerChannel < 0 || iMidiPort < 0)
1170     return LSCP_FAILED;
1171    
1172     sprintf(szQuery, "SET CHANNEL MIDI_INPUT_PORT %d %d\r\n", iSamplerChannel, iMidiPort);
1173     return lscp_client_query(pClient, szQuery);
1174     }
1175    
1176    
1177     /**
1178     * Setting MIDI input channel:
1179     * SET CHANNEL MIDI_INPUT_CHANNEL <sampler-channel> <midi-input-chan>
1180     *
1181     * @param pClient Pointer to client instance structure.
1182     * @param iSamplerChannel Sampler channel number.
1183     * @param iMidiChannel MIDI channel number to listen (1-16) or
1184     * zero (0) to listen on all channels.
1185     *
1186     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
1187     */
1188     lscp_status_t lscp_set_channel_midi_channel ( lscp_client_t *pClient, int iSamplerChannel, int iMidiChannel )
1189     {
1190     char szQuery[LSCP_BUFSIZ];
1191    
1192     if (iSamplerChannel < 0 || iMidiChannel < 0 || iMidiChannel > 16)
1193     return LSCP_FAILED;
1194    
1195     if (iMidiChannel > 0)
1196     sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d %d\r\n", iSamplerChannel, iMidiChannel);
1197     else
1198     sprintf(szQuery, "SET CHANNEL MIDI_INPUT_CHANNEL %d ALL\r\n", iSamplerChannel);
1199     return lscp_client_query(pClient, szQuery);
1200     }
1201    
1202    
1203     /**
1204     * Setting channel volume:
1205     * SET CHANNEL VOLUME <sampler-channel> <volume>
1206     *
1207     * @param pClient Pointer to client instance structure.
1208     * @param iSamplerChannel Sampler channel number.
1209     * @param fVolume Sampler channel volume as a positive floating point
1210     * number, where a value less than 1.0 for attenuation,
1211     * and greater than 1.0 for amplification.
1212     *
1213     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
1214     */
1215     lscp_status_t lscp_set_channel_volume ( lscp_client_t *pClient, int iSamplerChannel, float fVolume )
1216     {
1217     char szQuery[LSCP_BUFSIZ];
1218    
1219     if (iSamplerChannel < 0 || fVolume < 0.0)
1220     return LSCP_FAILED;
1221    
1222     sprintf(szQuery, "SET CHANNEL VOLUME %d %g\r\n", iSamplerChannel, fVolume);
1223     return lscp_client_query(pClient, szQuery);
1224     }
1225    
1226    
1227     /**
1228     * Resetting a sampler channel:
1229     * RESET CHANNEL <sampler-channel>
1230     *
1231     * @param pClient Pointer to client instance structure.
1232     * @param iSamplerChannel Sampler channel number.
1233     *
1234     * @returns LSCP_OK on success, LSCP_FAILED otherwise.
1235     */
1236     lscp_status_t lscp_reset_channel ( lscp_client_t *pClient, int iSamplerChannel )
1237     {
1238     char szQuery[LSCP_BUFSIZ];
1239    
1240     if (iSamplerChannel < 0)
1241     return LSCP_FAILED;
1242    
1243     sprintf(szQuery, "RESET CHANNEL %d\r\n", iSamplerChannel);
1244     return lscp_client_query(pClient, szQuery);
1245     }
1246    
1247    
1248     // end of client.c

  ViewVC Help
Powered by ViewVC