]> git.sesse.net Git - mlt/blob - src/valerie/valerie.c
miracle part 1
[mlt] / src / valerie / valerie.c
1 /*
2  * valerie.c -- High Level Client API for miracle
3  * Copyright (C) 2002-2003 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 /* System header files */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdarg.h>
26
27 /* Application header files */
28 #include "valerie.h"
29 #include "valerie_tokeniser.h"
30 #include "valerie_util.h"
31
32 /** Initialise the valerie structure.
33 */
34
35 valerie valerie_init( valerie_parser parser )
36 {
37         valerie this = malloc( sizeof( valerie_t ) );
38         if ( this != NULL )
39         {
40                 memset( this, 0, sizeof( valerie_t ) );
41                 this->parser = parser;
42         }
43         return this;
44 }
45
46 /** Set the response structure associated to the last command.
47 */
48
49 static void valerie_set_last_response( valerie this, valerie_response response )
50 {
51         if ( this != NULL )
52         {
53                 if ( this->last_response != NULL )
54                         valerie_response_close( this->last_response );
55                 this->last_response = response;
56         }
57 }
58
59 /** Connect to the parser.
60 */
61
62 valerie_error_code valerie_connect( valerie this )
63 {
64         valerie_error_code error = valerie_server_unavailable;
65         valerie_response response = valerie_parser_connect( this->parser );
66         if ( response != NULL )
67         {
68                 valerie_set_last_response( this, response );
69                 if ( valerie_response_get_error_code( response ) == 100 )
70                         error = valerie_ok;
71         }
72         return error;
73 }
74
75 /** Interpret a non-context sensitive error code.
76 */
77
78 static valerie_error_code valerie_get_error_code( valerie this, valerie_response response )
79 {
80         valerie_error_code error = valerie_server_unavailable;
81         switch( valerie_response_get_error_code( response ) )
82         {
83                 case -1:
84                         error = valerie_server_unavailable;
85                         break;
86                 case -2:
87                         error = valerie_no_response;
88                         break;
89                 case 200:
90                 case 201:
91                 case 202:
92                         error = valerie_ok;
93                         break;
94                 case 400:
95                         error = valerie_invalid_command;
96                         break;
97                 case 401:
98                         error = valerie_server_timeout;
99                         break;
100                 case 402:
101                         error = valerie_missing_argument;
102                         break;
103                 case 403:
104                         error = valerie_unit_unavailable;
105                         break;
106                 case 404:
107                         error = valerie_invalid_file;
108                         break;
109                 default:
110                 case 500:
111                         error = valerie_unknown_error;
112                         break;
113         }
114         return error;
115 }
116
117 /** Execute a command.
118 */
119
120 valerie_error_code valerie_execute( valerie this, size_t size, char *format, ... )
121 {
122         valerie_error_code error = valerie_server_unavailable;
123         char *command = malloc( size );
124         if ( this != NULL && command != NULL )
125         {
126                 va_list list;
127                 va_start( list, format );
128                 if ( vsnprintf( command, size, format, list ) != 0 )
129                 {
130                         valerie_response response = valerie_parser_execute( this->parser, command );
131                         valerie_set_last_response( this, response );
132                         error = valerie_get_error_code( this, response );
133                 }
134                 else
135                 {
136                         error = valerie_invalid_command;
137                 }
138                 va_end( list );
139         }
140         else
141         {
142                 error = valerie_malloc_failed;
143         }
144         free( command );
145         return error;
146 }
147
148 /** Set a global property.
149 */
150
151 valerie_error_code valerie_set( valerie this, char *property, char *value )
152 {
153         return valerie_execute( this, 1024, "SET %s=%s", property, value );
154 }
155
156 /** Get a global property.
157 */
158
159 valerie_error_code valerie_get( valerie this, char *property, char *value, int length )
160 {
161         valerie_error_code error = valerie_execute( this, 1024, "GET %s", property );
162         if ( error == valerie_ok )
163         {
164                 valerie_response response = valerie_get_last_response( this );
165                 strncpy( value, valerie_response_get_line( response, 1 ), length );
166         }
167         return error;
168 }
169
170 /** Run a script.
171 */
172
173 valerie_error_code valerie_run( valerie this, char *file )
174 {
175         return valerie_execute( this, 10240, "RUN \"%s\"", file );
176 }
177
178 /** Add a unit.
179 */
180
181 valerie_error_code valerie_unit_add( valerie this, char *guid, int *unit )
182 {
183         valerie_error_code error = valerie_execute( this, 1024, "UADD %s", guid );
184         if ( error == valerie_ok )
185         {
186                 int length = valerie_response_count( this->last_response );
187                 char *line = valerie_response_get_line( this->last_response, length - 2 );
188                 if ( line == NULL || sscanf( line, "U%d", unit ) != 1 )
189                         error = valerie_unit_creation_failed;
190         }
191         else
192         {
193                 if ( error == valerie_unknown_error )
194                         error = valerie_unit_creation_failed;
195         }
196         return error;
197 }
198
199 /** Load a file on the specified unit.
200 */
201
202 valerie_error_code valerie_unit_load( valerie this, int unit, char *file )
203 {
204         return valerie_execute( this, 10240, "LOAD U%d \"%s\"", unit, file );
205 }
206
207 static void valerie_interpret_clip_offset( char *output, valerie_clip_offset offset, int clip )
208 {
209         switch( offset )
210         {
211                 case valerie_absolute:
212                         sprintf( output, "%d", clip );
213                         break;
214                 case valerie_relative:
215                         if ( clip < 0 )
216                                 sprintf( output, "%d", clip );
217                         else
218                                 sprintf( output, "+%d", clip );
219                         break;
220         }
221 }
222
223 /** Load a file on the specified unit with the specified in/out points.
224 */
225
226 valerie_error_code valerie_unit_load_clipped( valerie this, int unit, char *file, double in, double out )
227 {
228         return valerie_execute( this, 10240, "LOAD U%d \"%s\" %e %e", unit, file, in, out );
229 }
230
231 /** Load a file on the specified unit at the end of the current pump.
232 */
233
234 valerie_error_code valerie_unit_load_back( valerie this, int unit, char *file )
235 {
236         return valerie_execute( this, 10240, "LOAD U%d \"!%s\"", unit, file );
237 }
238
239 /** Load a file on the specified unit at the end of the pump with the specified in/out points.
240 */
241
242 valerie_error_code valerie_unit_load_back_clipped( valerie this, int unit, char *file, double in, double out )
243 {
244         return valerie_execute( this, 10240, "LOAD U%d \"!%s\" %e %e", unit, file, in, out );
245 }
246
247 /** Append a file on the specified unit.
248 */
249
250 valerie_error_code valerie_unit_append( valerie this, int unit, char *file, double in, double out )
251 {
252         return valerie_execute( this, 10240, "APND U%d \"%s\" %e %e", unit, file, in, out );
253 }
254
255 /** Clean the unit - this function removes all but the currently playing clip.
256 */
257
258 valerie_error_code valerie_unit_clean( valerie this, int unit )
259 {
260         return valerie_execute( this, 1024, "CLEAN U%d", unit );
261 }
262
263 /** Move clips on the units playlist.
264 */
265
266 valerie_error_code valerie_unit_clip_move( valerie this, int unit, valerie_clip_offset src_offset, int src, valerie_clip_offset dest_offset, int dest )
267 {
268         char temp1[ 100 ];
269         char temp2[ 100 ];
270         valerie_interpret_clip_offset( temp1, src_offset, src );
271         valerie_interpret_clip_offset( temp2, dest_offset, dest );
272         return valerie_execute( this, 1024, "MOVE U%d %s %s", unit, temp1, temp2 );
273 }
274
275 /** Remove clip at the specified position.
276 */
277
278 valerie_error_code valerie_unit_clip_remove( valerie this, int unit, valerie_clip_offset offset, int clip )
279 {
280         char temp[ 100 ];
281         valerie_interpret_clip_offset( temp, offset, clip );
282         return valerie_execute( this, 1024, "REMOVE U%d %s", unit, temp );
283 }
284
285 /** Remove the currently playing clip.
286 */
287
288 valerie_error_code valerie_unit_remove_current_clip( valerie this, int unit )
289 {
290         return valerie_execute( this, 1024, "REMOVE U%d", unit );
291 }
292
293 /** Insert clip at the specified position.
294 */
295
296 valerie_error_code valerie_unit_clip_insert( valerie this, int unit, valerie_clip_offset offset, int clip, char *file, double in, double out )
297 {
298         char temp[ 100 ];
299         valerie_interpret_clip_offset( temp, offset, clip );
300         return valerie_execute( this, 1024, "INSERT U%d %s %s %e %e", unit, file, temp, in, out );
301 }
302
303 /** Play the unit at normal speed.
304 */
305
306 valerie_error_code valerie_unit_play( valerie this, int unit )
307 {
308         return valerie_execute( this, 1024, "PLAY U%d 1000", unit );
309 }
310
311 /** Play the unit at specified speed.
312 */
313
314 valerie_error_code valerie_unit_play_at_speed( valerie this, int unit, int speed )
315 {
316         return valerie_execute( this, 10240, "PLAY U%d %d", unit, speed );
317 }
318
319 /** Stop playback on the specified unit.
320 */
321
322 valerie_error_code valerie_unit_stop( valerie this, int unit )
323 {
324         return valerie_execute( this, 1024, "STOP U%d", unit );
325 }
326
327 /** Pause playback on the specified unit.
328 */
329
330 valerie_error_code valerie_unit_pause( valerie this, int unit )
331 {
332         return valerie_execute( this, 1024, "PAUSE U%d", unit );
333 }
334
335 /** Rewind the specified unit.
336 */
337
338 valerie_error_code valerie_unit_rewind( valerie this, int unit )
339 {
340         return valerie_execute( this, 1024, "REW U%d", unit );
341 }
342
343 /** Fast forward the specified unit.
344 */
345
346 valerie_error_code valerie_unit_fast_forward( valerie this, int unit )
347 {
348         return valerie_execute( this, 1024, "FF U%d", unit );
349 }
350
351 /** Step by the number of frames on the specified unit.
352 */
353
354 valerie_error_code valerie_unit_step( valerie this, int unit, double step )
355 {
356         return valerie_execute( this, 1024, "STEP U%d %e", unit, step );
357 }
358
359 /** Goto the specified frame on the specified unit.
360 */
361
362 valerie_error_code valerie_unit_goto( valerie this, int unit, double position )
363 {
364         return valerie_execute( this, 1024, "GOTO U%d %e", unit, position );
365 }
366
367 /** Goto the specified frame in the clip on the specified unit.
368 */
369
370 valerie_error_code valerie_unit_clip_goto( valerie this, int unit, valerie_clip_offset offset, int clip, double position )
371 {
372         char temp[ 100 ];
373         valerie_interpret_clip_offset( temp, offset, clip );
374         return valerie_execute( this, 1024, "GOTO U%d %e %s", unit, position, temp );
375 }
376
377 /** Set the in point of the loaded file on the specified unit.
378 */
379
380 valerie_error_code valerie_unit_set_in( valerie this, int unit, double in )
381 {
382         return valerie_execute( this, 1024, "SIN U%d %e", unit, in );
383 }
384
385 /** Set the in point of the clip on the specified unit.
386 */
387
388 valerie_error_code valerie_unit_clip_set_in( valerie this, int unit, valerie_clip_offset offset, int clip, double in )
389 {
390         char temp[ 100 ];
391         valerie_interpret_clip_offset( temp, offset, clip );
392         return valerie_execute( this, 1024, "SIN U%d %e %s", unit, in, temp );
393 }
394
395 /** Set the out point of the loaded file on the specified unit.
396 */
397
398 valerie_error_code valerie_unit_set_out( valerie this, int unit, double out )
399 {
400         return valerie_execute( this, 1024, "SOUT U%d %e", unit, out );
401 }
402
403 /** Set the out point of the clip on the specified unit.
404 */
405
406 valerie_error_code valerie_unit_clip_set_out( valerie this, int unit, valerie_clip_offset offset, int clip, double in )
407 {
408         char temp[ 100 ];
409         valerie_interpret_clip_offset( temp, offset, clip );
410         return valerie_execute( this, 1024, "SOUT U%d %e %s", unit, in, temp );
411 }
412
413 /** Clear the in point of the loaded file on the specified unit.
414 */
415
416 valerie_error_code valerie_unit_clear_in( valerie this, int unit )
417 {
418         return valerie_execute( this, 1024, "SIN U%d -1", unit );
419 }
420
421 /** Clear the out point of the loaded file on the specified unit.
422 */
423
424 valerie_error_code valerie_unit_clear_out( valerie this, int unit )
425 {
426         return valerie_execute( this, 1024, "SOUT U%d -1", unit );
427 }
428
429 /** Clear the in and out points on the loaded file on the specified unit.
430 */
431
432 valerie_error_code valerie_unit_clear_in_out( valerie this, int unit )
433 {
434         valerie_error_code error = valerie_unit_clear_out( this, unit );
435         if ( error == valerie_ok )
436                 error = valerie_unit_clear_in( this, unit );
437         return error;
438 }
439
440 /** Set a unit configuration property.
441 */
442
443 valerie_error_code valerie_unit_set( valerie this, int unit, char *name, char *value )
444 {
445         return valerie_execute( this, 1024, "USET U%d %s=%s", unit, name, value );
446 }
447
448 /** Get a unit configuration property.
449 */
450
451 valerie_error_code valerie_unit_get( valerie this, int unit, char *name )
452 {
453         return valerie_execute( this, 1024, "UGET U%d %s", unit, name );
454 }
455
456 /** Get a units status.
457 */
458
459 valerie_error_code valerie_unit_status( valerie this, int unit, valerie_status status )
460 {
461         valerie_error_code error = valerie_execute( this, 1024, "USTA U%d", unit );
462         int error_code = valerie_response_get_error_code( this->last_response );
463
464         memset( status, 0, sizeof( valerie_status_t ) );
465         status->unit = unit;
466         if ( error_code == 202 && valerie_response_count( this->last_response ) == 2 )
467                 valerie_status_parse( status, valerie_response_get_line( this->last_response, 1 ) );
468         else if ( error_code == 403 )
469                 status->status = unit_undefined;
470
471         return error;
472 }
473
474 /** Transfer the current settings of unit src to unit dest.
475 */
476
477 valerie_error_code valerie_unit_transfer( valerie this, int src, int dest )
478 {
479         return valerie_execute( this, 1024, "XFER U%d U%d", src, dest );
480 }
481
482 /** Obtain the parsers notifier.
483 */
484
485 valerie_notifier valerie_get_notifier( valerie this )
486 {
487         if ( this != NULL )
488                 return valerie_parser_get_notifier( this->parser );
489         else
490                 return NULL;
491 }
492
493 /** List the contents of the specified directory.
494 */
495
496 valerie_dir valerie_dir_init( valerie this, char *directory )
497 {
498         valerie_dir dir = malloc( sizeof( valerie_dir_t ) );
499         if ( dir != NULL )
500         {
501                 memset( dir, 0, sizeof( valerie_dir_t ) );
502                 dir->directory = strdup( directory );
503                 dir->response = valerie_parser_executef( this->parser, "CLS \"%s\"", directory );
504         }
505         return dir;
506 }
507
508 /** Return the error code associated to the dir.
509 */
510
511 valerie_error_code valerie_dir_get_error_code( valerie_dir dir )
512 {
513         if ( dir != NULL )
514                 return valerie_get_error_code( NULL, dir->response );
515         else
516                 return valerie_malloc_failed;
517 }
518
519 /** Get a particular file entry in the directory.
520 */
521
522 valerie_error_code valerie_dir_get( valerie_dir dir, int index, valerie_dir_entry entry )
523 {
524         valerie_error_code error = valerie_ok;
525         memset( entry, 0, sizeof( valerie_dir_entry_t ) );
526         if ( index < valerie_dir_count( dir ) )
527         {
528                 char *line = valerie_response_get_line( dir->response, index + 1 );
529                 valerie_tokeniser tokeniser = valerie_tokeniser_init( );
530                 valerie_tokeniser_parse_new( tokeniser, line, " " );
531
532                 if ( valerie_tokeniser_count( tokeniser ) > 0 )
533                 {
534                         valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 0 ), '\"' );
535                         strcpy( entry->full, dir->directory );
536                         if ( entry->full[ strlen( entry->full ) - 1 ] != '/' )
537                                 strcat( entry->full, "/" );
538                         strcpy( entry->name, valerie_tokeniser_get_string( tokeniser, 0 ) );
539                         strcat( entry->full, entry->name );
540
541                         switch ( valerie_tokeniser_count( tokeniser ) )
542                         {
543                                 case 1:
544                                         entry->dir = 1;
545                                         break;
546                                 case 2:
547                                         entry->size = strtoull( valerie_tokeniser_get_string( tokeniser, 1 ), NULL, 10 );
548                                         break;
549                                 default:
550                                         error = valerie_invalid_file;
551                                         break;
552                         }
553                 }
554                 valerie_tokeniser_close( tokeniser );
555         }
556         return error;
557 }
558
559 /** Get the number of entries in the directory
560 */
561
562 int valerie_dir_count( valerie_dir dir )
563 {
564         if ( dir != NULL && valerie_response_count( dir->response ) >= 2 )
565                 return valerie_response_count( dir->response ) - 2;
566         else
567                 return -1;
568 }
569
570 /** Close the directory structure.
571 */
572
573 void valerie_dir_close( valerie_dir dir )
574 {
575         if ( dir != NULL )
576         {
577                 free( dir->directory );
578                 valerie_response_close( dir->response );
579                 free( dir );
580         }
581 }
582
583 /** List the playlist of the specified unit.
584 */
585
586 valerie_list valerie_list_init( valerie this, int unit )
587 {
588         valerie_list list = calloc( 1, sizeof( valerie_list_t ) );
589         if ( list != NULL )
590         {
591                 list->response = valerie_parser_executef( this->parser, "LIST U%d", unit );
592                 if ( valerie_response_count( list->response ) >= 2 )
593                         list->generation = atoi( valerie_response_get_line( list->response, 1 ) );
594         }
595         return list;
596 }
597
598 /** Return the error code associated to the list.
599 */
600
601 valerie_error_code valerie_list_get_error_code( valerie_list list )
602 {
603         if ( list != NULL )
604                 return valerie_get_error_code( NULL, list->response );
605         else
606                 return valerie_malloc_failed;
607 }
608
609 /** Get a particular file entry in the list.
610 */
611
612 valerie_error_code valerie_list_get( valerie_list list, int index, valerie_list_entry entry )
613 {
614         valerie_error_code error = valerie_ok;
615         memset( entry, 0, sizeof( valerie_list_entry_t ) );
616         if ( index < valerie_list_count( list ) )
617         {
618                 char *line = valerie_response_get_line( list->response, index + 2 );
619                 valerie_tokeniser tokeniser = valerie_tokeniser_init( );
620                 valerie_tokeniser_parse_new( tokeniser, line, " " );
621
622                 if ( valerie_tokeniser_count( tokeniser ) > 0 )
623                 {
624                         entry->clip = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
625                         valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 1 ), '\"' );
626                         strcpy( entry->full, valerie_tokeniser_get_string( tokeniser, 1 ) );
627                         entry->in = atof( valerie_tokeniser_get_string( tokeniser, 2 ) );
628                         entry->out = atof( valerie_tokeniser_get_string( tokeniser, 3 ) );
629                         entry->max = atof( valerie_tokeniser_get_string( tokeniser, 4 ) );
630                         entry->size = atof( valerie_tokeniser_get_string( tokeniser, 5 ) );
631                         entry->fps = atof( valerie_tokeniser_get_string( tokeniser, 6 ) );
632                 }
633                 valerie_tokeniser_close( tokeniser );
634         }
635         return error;
636 }
637
638 /** Get the number of entries in the list
639 */
640
641 int valerie_list_count( valerie_list list )
642 {
643         if ( list != NULL && valerie_response_count( list->response ) >= 3 )
644                 return valerie_response_count( list->response ) - 3;
645         else
646                 return -1;
647 }
648
649 /** Close the list structure.
650 */
651
652 void valerie_list_close( valerie_list list )
653 {
654         if ( list != NULL )
655         {
656                 valerie_response_close( list->response );
657                 free( list );
658         }
659 }
660
661 /** List the currently connected nodes.
662 */
663
664 valerie_nodes valerie_nodes_init( valerie this )
665 {
666         valerie_nodes nodes = malloc( sizeof( valerie_nodes_t ) );
667         if ( nodes != NULL )
668         {
669                 memset( nodes, 0, sizeof( valerie_nodes_t ) );
670                 nodes->response = valerie_parser_executef( this->parser, "NLS" );
671         }
672         return nodes;
673 }
674
675 /** Return the error code associated to the nodes list.
676 */
677
678 valerie_error_code valerie_nodes_get_error_code( valerie_nodes nodes )
679 {
680         if ( nodes != NULL )
681                 return valerie_get_error_code( NULL, nodes->response );
682         else
683                 return valerie_malloc_failed;
684 }
685
686 /** Get a particular node entry.
687 */
688
689 valerie_error_code valerie_nodes_get( valerie_nodes nodes, int index, valerie_node_entry entry )
690 {
691         valerie_error_code error = valerie_ok;
692         memset( entry, 0, sizeof( valerie_node_entry_t ) );
693         if ( index < valerie_nodes_count( nodes ) )
694         {
695                 char *line = valerie_response_get_line( nodes->response, index + 1 );
696                 valerie_tokeniser tokeniser = valerie_tokeniser_init( );
697                 valerie_tokeniser_parse_new( tokeniser, line, " " );
698
699                 if ( valerie_tokeniser_count( tokeniser ) == 3 )
700                 {
701                         entry->node = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) );
702                         strncpy( entry->guid, valerie_tokeniser_get_string( tokeniser, 1 ), sizeof( entry->guid ) );
703                         valerie_util_strip( valerie_tokeniser_get_string( tokeniser, 2 ), '\"' );
704                         strncpy( entry->name, valerie_tokeniser_get_string( tokeniser, 2 ), sizeof( entry->name ) );
705                 }
706
707                 valerie_tokeniser_close( tokeniser );
708         }
709         return error;
710 }
711
712 /** Get the number of nodes
713 */
714
715 int valerie_nodes_count( valerie_nodes nodes )
716 {
717         if ( nodes != NULL && valerie_response_count( nodes->response ) >= 2 )
718                 return valerie_response_count( nodes->response ) - 2;
719         else
720                 return -1;
721 }
722
723 /** Close the nodes structure.
724 */
725
726 void valerie_nodes_close( valerie_nodes nodes )
727 {
728         if ( nodes != NULL )
729         {
730                 valerie_response_close( nodes->response );
731                 free( nodes );
732         }
733 }
734
735 /** List the currently defined units.
736 */
737
738 valerie_units valerie_units_init( valerie this )
739 {
740         valerie_units units = malloc( sizeof( valerie_units_t ) );
741         if ( units != NULL )
742         {
743                 memset( units, 0, sizeof( valerie_units_t ) );
744                 units->response = valerie_parser_executef( this->parser, "ULS" );
745         }
746         return units;
747 }
748
749 /** Return the error code associated to the nodes list.
750 */
751
752 valerie_error_code valerie_units_get_error_code( valerie_units units )
753 {
754         if ( units != NULL )
755                 return valerie_get_error_code( NULL, units->response );
756         else
757                 return valerie_malloc_failed;
758 }
759
760 /** Get a particular unit entry.
761 */
762
763 valerie_error_code valerie_units_get( valerie_units units, int index, valerie_unit_entry entry )
764 {
765         valerie_error_code error = valerie_ok;
766         memset( entry, 0, sizeof( valerie_unit_entry_t ) );
767         if ( index < valerie_units_count( units ) )
768         {
769                 char *line = valerie_response_get_line( units->response, index + 1 );
770                 valerie_tokeniser tokeniser = valerie_tokeniser_init( );
771                 valerie_tokeniser_parse_new( tokeniser, line, " " );
772
773                 if ( valerie_tokeniser_count( tokeniser ) == 4 )
774                 {
775                         entry->unit = atoi( valerie_tokeniser_get_string( tokeniser, 0 ) + 1 );
776                         entry->node = atoi( valerie_tokeniser_get_string( tokeniser, 1 ) );
777                         strncpy( entry->guid, valerie_tokeniser_get_string( tokeniser, 2 ), sizeof( entry->guid ) );
778                         entry->online = atoi( valerie_tokeniser_get_string( tokeniser, 3 ) );
779                 }
780
781                 valerie_tokeniser_close( tokeniser );
782         }
783         return error;
784 }
785
786 /** Get the number of units
787 */
788
789 int valerie_units_count( valerie_units units )
790 {
791         if ( units != NULL && valerie_response_count( units->response ) >= 2 )
792                 return valerie_response_count( units->response ) - 2;
793         else
794                 return -1;
795 }
796
797 /** Close the units structure.
798 */
799
800 void valerie_units_close( valerie_units units )
801 {
802         if ( units != NULL )
803         {
804                 valerie_response_close( units->response );
805                 free( units );
806         }
807 }
808
809 /** Get the response of the last command executed.
810 */
811
812 valerie_response valerie_get_last_response( valerie this )
813 {
814         return this->last_response;
815 }
816
817 /** Obtain a printable message associated to the error code provided.
818 */
819
820 char *valerie_error_description( valerie_error_code error )
821 {
822         char *msg = "Unrecognised error";
823         switch( error )
824         {
825                 case valerie_ok:
826                         msg = "OK";
827                         break;
828                 case valerie_malloc_failed:
829                         msg = "Memory allocation error";
830                         break;
831                 case valerie_unknown_error:
832                         msg = "Unknown error";
833                         break;
834                 case valerie_no_response:
835                         msg = "No response obtained";
836                         break;
837                 case valerie_invalid_command:
838                         msg = "Invalid command";
839                         break;
840                 case valerie_server_timeout:
841                         msg = "Communications with server timed out";
842                         break;
843                 case valerie_missing_argument:
844                         msg = "Missing argument";
845                         break;
846                 case valerie_server_unavailable:
847                         msg = "Unable to communicate with server";
848                         break;
849                 case valerie_unit_creation_failed:
850                         msg = "Unit creation failed";
851                         break;
852                 case valerie_unit_unavailable:
853                         msg = "Unit unavailable";
854                         break;
855                 case valerie_invalid_file:
856                         msg = "Invalid file";
857                         break;
858                 case valerie_invalid_position:
859                         msg = "Invalid position";
860                         break;
861         }
862         return msg;
863 }
864
865 /** Close the valerie structure.
866 */
867
868 void valerie_close( valerie this )
869 {
870         if ( this != NULL )
871         {
872                 valerie_set_last_response( this, NULL );
873                 free( this );
874         }
875 }