File Coverage

blib/lib/CWB/CQI/IOClient.pm
Criterion Covered Total %
statement 21 485 4.3
branch 0 102 0.0
condition 0 63 0.0
subroutine 7 78 8.9
pod 0 71 0.0
total 28 799 3.5


line stmt bran cond sub pod time code
1             package CWB::CQI::IOClient;
2             # -*-cperl-*-
3              
4 2     2   75858 use strict;
  2         13  
  2         66  
5 2     2   10 use warnings;
  2         4  
  2         54  
6              
7 2     2   1252 use CWB::CQI;
  2         8  
  2         58  
8 2     2   1000 use IO::Socket;
  2         47756  
  2         10  
9 2     2   1916 use FileHandle;
  2         7207  
  2         12  
10 2     2   704 use Carp;
  2         5  
  2         139  
11              
12             # export CQi client functions
13 2     2   18 use base qw(Exporter);
  2         6  
  2         12767  
14             our @EXPORT = (
15             qw,
16             qw,
17             qw,
18             qw,
19             );
20              
21             =head1 NAME
22              
23             CWB::CQI::IOClient - Alternative CQi client library (based on IO::Socket)
24              
25              
26             =head1 DESCRIPTION
27              
28             This is an alternative version of the B library, which uses the object-oriented
29             B module instead of calling low-level socket functions. It is a direct replacement and
30             presents exactly the same API, so one can simply change
31              
32             use CWB::CQI::Client;
33              
34             to
35              
36             use CWB::CQI::IOClient;
37              
38             in any CQi client script. See the L manpage for more information and a description
39             of available CQi functions.
40              
41             =cut
42              
43              
44             our $conn = new FileHandle;
45              
46             #
47             #
48             # Error Handling
49             #
50             #
51              
52             our $LastCmd = ""; # keep track of last command in case we receive an error code
53              
54             sub CqiError (@) {
55 0     0 0   foreach (@_) {
56 0           print STDERR "CQi ERROR: $_\n";
57             }
58 0           croak "CQI::Client -- connection aborted.";
59 0           exit 1; # Perl/Tk seems to catch the croak ...
60             }
61              
62             sub CqiErrorCode ($) {
63 0     0 0   my $errcode = shift;
64 0           my $group = $errcode >> 8;
65 0           my $command = $errcode & 0xff;
66 0           my $errhex = sprintf "%02X:%02X", $group, $command;
67 0           my $name = $CWB::CQI::CommandName{$errcode};
68            
69 0 0         if ($name =~ /ERROR/) {
70 0           CqiError "Received $name [$errhex] in response to", "$LastCmd";
71             }
72             else {
73 0           CqiError "Unexpected response $name [$errhex] to", "$LastCmd";
74             }
75             }
76              
77             sub CqiCheckResponse ($@) {
78 0     0 0   my $response = shift;
79 0           my %expect = map { $_ => 1 } @_;
  0            
80            
81             CqiErrorCode $response
82 0 0         unless defined $expect{$response};
83             }
84              
85              
86             #
87             #
88             # Connect to CQi server / Disconnect
89             #
90             #
91              
92             sub cqi_connect {
93 0     0 0   my $user = shift;
94 0           my $passwd = shift;
95 0           my $host = shift; # optional
96 0           my $port = shift; # optional
97              
98 0 0         $host = 'localhost'
99             unless defined $host;
100 0 0         $port = $CWB::CQI::PORT
101             unless defined $port;
102              
103 0 0 0       croak "USAGE: cqi_connect(username, password, [, remotehost [, port]]);"
104             unless defined $user and defined $passwd;
105 0           $LastCmd = "CQI_CTRL_CONNECT($user, '$passwd', $host, $port)";
106              
107 0           my $ipaddr = inet_aton($host);
108 0           my $sockaddr = sockaddr_in($port, $ipaddr);
109 0           my $protocol = getprotobyname('tcp');
110              
111             $conn = new IO::Socket 'Domain' => AF_INET, 'Type' => SOCK_STREAM, 'Proto' => "tcp", 'PeerHost' => $host, 'PeerPort' => $port
112 0 0         or do { croak "cqi_connect(): $!", exit 1};
  0            
113 0           $conn->autoflush(0);
114              
115 0           cqi_send_word($CWB::CQI::CTRL_CONNECT);
116 0           cqi_send_string($user);
117 0           cqi_send_string($passwd);
118 0           cqi_flush();
119              
120 0           my $response = cqi_read_word();
121 0           CqiCheckResponse $response, $CWB::CQI::STATUS_CONNECT_OK;
122             }
123              
124             sub cqi_bye {
125 0     0 0   $LastCmd = "CQI_CTRL_BYE()";
126 0           cqi_send_word($CWB::CQI::CTRL_BYE);
127 0           cqi_flush();
128 0           my $response = cqi_read_word();
129 0           CqiCheckResponse $response, $CWB::CQI::STATUS_BYE_OK;
130 0           $conn->close;
131 0           $conn = undef;
132             }
133              
134             sub cqi_ping {
135 0     0 0   $LastCmd = "CQI_CTRL_PING()";
136 0           cqi_send_word($CWB::CQI::CTRL_PING);
137 0           cqi_flush();
138 0           CqiCheckResponse cqi_read_word(), $CWB::CQI::STATUS_PING_OK;
139             }
140              
141              
142             #
143             #
144             # CQi Commands
145             #
146             #
147              
148             sub cqi_ask_feature {
149 0     0 0   my $feature = lc shift;
150 0           my %features = (
151             "cqi1.0" => $CWB::CQI::ASK_FEATURE_CQI_1_0,
152             "cl2.3" => $CWB::CQI::ASK_FEATURE_CL_2_3,
153             "cqp2.3" => $CWB::CQI::ASK_FEATURE_CQP_2_3,
154             );
155             croak "USAGE: \$supported = cqi_ask_feature('cqi1.0' | 'cl2.3' | 'cqp2.3');"
156 0 0         unless defined $features{$feature};
157 0           $LastCmd = $CWB::CQI::CommandName{$features{$feature}} . "()";
158 0           cqi_send_word($features{$feature});
159 0           cqi_flush();
160 0           return cqi_expect_bool();
161             }
162              
163             sub cqi_list_corpora {
164 0     0 0   $LastCmd = "CQI_CORPUS_LIST_CORPORA()";
165 0 0         croak "USAGE: \@corpora = cqi_list_corpora();"
166             unless @_ == 0;
167 0           cqi_send_word($CWB::CQI::CORPUS_LIST_CORPORA);
168 0           cqi_flush();
169 0           return cqi_expect_string_list();
170             }
171              
172             sub cqi_charset {
173 0     0 0   my $corpus = shift;
174 0           $LastCmd = "CQI_CORPUS_CHARSET($corpus)";
175 0           cqi_send_word($CWB::CQI::CORPUS_CHARSET);
176 0           cqi_send_string($corpus);
177 0           cqi_flush();
178 0           return cqi_expect_string();
179             }
180              
181             sub cqi_properties {
182 0     0 0   my $corpus = shift;
183 0           $LastCmd = "CQI_CORPUS_PROPERTIES($corpus)";
184 0           cqi_send_word($CWB::CQI::CORPUS_PROPERTIES);
185 0           cqi_send_string($corpus);
186 0           cqi_flush();
187 0           return cqi_expect_string_list();
188             }
189              
190             sub cqi_attributes {
191 0     0 0   my $corpus = shift;
192 0           my $type = shift;
193 0           my %types = (
194             'p' => $CWB::CQI::CORPUS_POSITIONAL_ATTRIBUTES,
195             's' => $CWB::CQI::CORPUS_STRUCTURAL_ATTRIBUTES,
196             'a' => $CWB::CQI::CORPUS_ALIGNMENT_ATTRIBUTES,
197             );
198             croak "USAGE: \@attributes = cqi_attributes(\$corpus, ('p'|'s'|'a'));"
199 0 0         unless defined $types{$type};
200 0           $LastCmd = $CWB::CQI::CommandName{$types{$type}} . "($corpus)";
201 0           cqi_send_word($types{$type});
202 0           cqi_send_string($corpus);
203 0           cqi_flush();
204 0           return cqi_expect_string_list();
205             }
206              
207             sub cqi_structural_attribute_has_values {
208 0     0 0   my $attribute = shift;
209 0           $LastCmd = "CQI_CORPUS_STRUCTURAL_ATTRIBUTE_HAS_VALUES($attribute)";
210 0           cqi_send_word($CWB::CQI::CORPUS_STRUCTURAL_ATTRIBUTE_HAS_VALUES);
211 0           cqi_send_string($attribute);
212 0           cqi_flush();
213 0           return cqi_expect_bool();
214             }
215              
216             sub cqi_full_name {
217 0     0 0   my $corpus = shift;
218 0           $LastCmd = "CQI_CORPUS_FULL_NAME($corpus)";
219 0           cqi_send_word($CWB::CQI::CORPUS_FULL_NAME);
220 0           cqi_send_string($corpus);
221 0           cqi_flush();
222 0           return cqi_expect_string();
223             }
224              
225             sub cqi_corpus_info {
226 0     0 0   my $corpus = shift;
227 0           $LastCmd = "CQI_CORPUS_INFO($corpus)";
228 0           cqi_send_word($CWB::CQI::CORPUS_INFO);
229 0           cqi_send_string($corpus);
230 0           cqi_flush();
231 0           return cqi_expect_string_list();
232             }
233              
234             sub cqi_drop_corpus {
235 0     0 0   my $corpus = shift;
236 0           $LastCmd = "CQI_CORPUS_DROP_CORPUS($corpus)";
237 0           cqi_send_word($CWB::CQI::CORPUS_DROP_CORPUS);
238 0           cqi_send_string($corpus);
239 0           cqi_flush();
240 0           cqi_expect_status($CWB::CQI::STATUS_OK);
241             }
242              
243             sub cqi_attribute_size {
244 0     0 0   my $attribute = shift;
245 0           $LastCmd = "CQI_CL_ATTRIBUTE_SIZE($attribute)";
246 0           cqi_send_word($CWB::CQI::CL_ATTRIBUTE_SIZE);
247 0           cqi_send_string($attribute);
248 0           cqi_flush();
249 0           return cqi_expect_int();
250             }
251              
252             sub cqi_lexicon_size {
253 0     0 0   my $attribute = shift;
254 0           $LastCmd = "CQI_CL_LEXICON_SIZE($attribute)";
255 0           cqi_send_word($CWB::CQI::CL_LEXICON_SIZE);
256 0           cqi_send_string($attribute);
257 0           cqi_flush();
258 0           return cqi_expect_int();
259             }
260              
261             sub cqi_drop_attribute {
262 0     0 0   my $attribute = shift;
263 0           $LastCmd = "CQI_CL_DROP_ATTRIBUTE($attribute)";
264 0           cqi_send_word($CWB::CQI::CL_DROP_ATTRIBUTE);
265 0           cqi_send_string($attribute);
266 0           cqi_flush();
267 0           cqi_expect_status($CWB::CQI::STATUS_OK);
268             }
269              
270             # 'scalar' functions which map to lists in the CQi are wrapped
271             # in a scalar-safe client interface, so we CAN use them with simple
272             # scalars in CQI::Client.
273             sub cqi_str2id {
274 0     0 0   my $attribute = shift;
275 0           $LastCmd = "CQI_CL_STR2ID($attribute, [@_])";
276 0           cqi_send_word($CWB::CQI::CL_STR2ID);
277 0           cqi_send_string($attribute);
278 0           cqi_send_string_list(@_);
279 0           cqi_flush();
280 0           my @list = cqi_expect_int_list();
281 0 0         if (wantarray) {
282 0           return @list;
283             }
284             else {
285 0 0         croak "Called in scalar context with list argument." unless @list == 1;
286 0           return $list[0];
287             }
288             }
289              
290             sub cqi_id2str {
291 0     0 0   my $attribute = shift;
292 0           $LastCmd = "CQI_CL_ID2STR($attribute, [@_])";
293 0           cqi_send_word($CWB::CQI::CL_ID2STR);
294 0           cqi_send_string($attribute);
295 0           cqi_send_int_list(@_);
296 0           cqi_flush();
297 0           my @list = cqi_expect_string_list();
298 0 0         if (wantarray) {
299 0           return @list;
300             }
301             else {
302 0 0         croak "Called in scalar context with list argument." unless @list == 1;
303 0           return $list[0];
304             }
305             }
306              
307             sub cqi_id2freq {
308 0     0 0   my $attribute = shift;
309 0           $LastCmd = "CQI_CL_ID2FREQ($attribute, [@_])";
310 0           cqi_send_word($CWB::CQI::CL_ID2FREQ);
311 0           cqi_send_string($attribute);
312 0           cqi_send_int_list(@_);
313 0           cqi_flush();
314 0           my @list = cqi_expect_int_list();
315 0 0         if (wantarray) {
316 0           return @list;
317             }
318             else {
319 0 0         croak "Called in scalar context with list argument." unless @list == 1;
320 0           return $list[0];
321             }
322             }
323              
324             sub cqi_cpos2id {
325 0     0 0   my $attribute = shift;
326 0           $LastCmd = "CQI_CL_CPOS2ID($attribute, [@_])";
327 0           cqi_send_word($CWB::CQI::CL_CPOS2ID);
328 0           cqi_send_string($attribute);
329 0           cqi_send_int_list(@_);
330 0           cqi_flush();
331 0           my @list = cqi_expect_int_list();
332 0 0         if (wantarray) {
333 0           return @list;
334             }
335             else {
336 0 0         croak "Called in scalar context with list argument." unless @list == 1;
337 0           return $list[0];
338             }
339             }
340              
341             sub cqi_cpos2str {
342 0     0 0   my $attribute = shift;
343 0           $LastCmd = "CQI_CL_CPOS2STR($attribute, [@_])";
344 0           cqi_send_word($CWB::CQI::CL_CPOS2STR);
345 0           cqi_send_string($attribute);
346 0           cqi_send_int_list(@_);
347 0           cqi_flush();
348 0           my @list = cqi_expect_string_list();
349 0 0         if (wantarray) {
350 0           return @list;
351             }
352             else {
353 0 0         croak "Called in scalar context with list argument." unless @list == 1;
354 0           return $list[0];
355             }
356             }
357              
358             sub cqi_cpos2struc {
359 0     0 0   my $attribute = shift;
360 0           $LastCmd = "CQI_CL_CPOS2STRUC($attribute, [@_])";
361 0           cqi_send_word($CWB::CQI::CL_CPOS2STRUC);
362 0           cqi_send_string($attribute);
363 0           cqi_send_int_list(@_);
364 0           cqi_flush();
365 0           my @list = cqi_expect_int_list();
366 0 0         if (wantarray) {
367 0           return @list;
368             }
369             else {
370 0 0         croak "Called in scalar context with list argument." unless @list == 1;
371 0           return $list[0];
372             }
373             }
374              
375             sub cqi_cpos2lbound {
376 0     0 0   my $attribute = shift;
377 0           $LastCmd = "CQI_CL_CPOS2LBOUND($attribute, [@_])";
378 0           cqi_send_word($CWB::CQI::CL_CPOS2LBOUND);
379 0           cqi_send_string($attribute);
380 0           cqi_send_int_list(@_);
381 0           cqi_flush();
382 0           my @list = cqi_expect_int_list();
383 0 0         if (wantarray) {
384 0           return @list;
385             }
386             else {
387 0 0         croak "Called in scalar context with list argument." unless @list == 1;
388 0           return $list[0];
389             }
390             }
391              
392             sub cqi_cpos2rbound {
393 0     0 0   my $attribute = shift;
394 0           $LastCmd = "CQI_CL_CPOS2RBOUND($attribute, [@_])";
395 0           cqi_send_word($CWB::CQI::CL_CPOS2RBOUND);
396 0           cqi_send_string($attribute);
397 0           cqi_send_int_list(@_);
398 0           cqi_flush();
399 0           my @list = cqi_expect_int_list();
400 0 0         if (wantarray) {
401 0           return @list;
402             }
403             else {
404 0 0         croak "Called in scalar context with list argument." unless @list == 1;
405 0           return $list[0];
406             }
407             }
408              
409             sub cqi_cpos2alg {
410 0     0 0   my $attribute = shift;
411 0           $LastCmd = "CQI_CL_CPOS2ALG($attribute, [@_])";
412 0           cqi_send_word($CWB::CQI::CL_CPOS2ALG);
413 0           cqi_send_string($attribute);
414 0           cqi_send_int_list(@_);
415 0           cqi_flush();
416 0           my @list = cqi_expect_int_list();
417 0 0         if (wantarray) {
418 0           return @list;
419             }
420             else {
421 0 0         croak "Called in scalar context with list argument." unless @list == 1;
422 0           return $list[0];
423             }
424             }
425              
426             sub cqi_struc2str {
427 0     0 0   my $attribute = shift;
428 0           $LastCmd = "CQI_CL_STRUC2STR($attribute, [@_])";
429 0           cqi_send_word($CWB::CQI::CL_STRUC2STR);
430 0           cqi_send_string($attribute);
431 0           cqi_send_int_list(@_);
432 0           cqi_flush();
433 0           my @list = cqi_expect_string_list();
434 0 0         if (wantarray) {
435 0           return @list;
436             }
437             else {
438 0 0         croak "Called in scalar context with list argument." unless @list == 1;
439 0           return $list[0];
440             }
441             }
442              
443             sub cqi_id2cpos {
444 0 0 0 0 0   croak "USAGE: \@cposlist = cqi_id2cpos(\$attribute, \$id);"
445             unless @_ == 2 and wantarray;
446 0           my $attribute = shift;
447 0           my $id = shift;
448              
449 0           $LastCmd = "CQI_CL_ID2CPOS($attribute, $id)";
450 0           cqi_send_word($CWB::CQI::CL_ID2CPOS);
451 0           cqi_send_string($attribute);
452 0           cqi_send_int($id);
453 0           cqi_flush();
454 0           return cqi_expect_int_list();
455             }
456              
457             sub cqi_idlist2cpos {
458 0     0 0   my $attribute = shift;
459 0           $LastCmd = "CQI_CL_IDLIST2CPOS($attribute, [@_])";
460 0           cqi_send_word($CWB::CQI::CL_IDLIST2CPOS);
461 0           cqi_send_string($attribute);
462 0           cqi_send_int_list(@_);
463 0           cqi_flush();
464 0           return cqi_expect_int_list();
465             }
466              
467             sub cqi_regex2id {
468 0 0 0 0 0   croak "USAGE: \@idlist = cqi_regex2id(\$attribute, \$regex);"
469             unless @_ == 2 and wantarray;
470 0           my $attribute = shift;
471 0           my $regex = shift;
472              
473 0           $LastCmd = "CQI_CL_REGEX2ID($attribute, $regex)";
474 0           cqi_send_word($CWB::CQI::CL_REGEX2ID);
475 0           cqi_send_string($attribute);
476 0           cqi_send_string($regex);
477 0           cqi_flush();
478 0           return cqi_expect_int_list();
479             }
480              
481             sub cqi_struc2cpos {
482 0 0 0 0 0   croak "USAGE: (\$start, \$end) = cqi_struc2cpos(\$attribute, \$struc);"
483             unless @_ == 2 and wantarray;
484 0           my $attribute = shift;
485 0           my $struc = shift;
486              
487 0           $LastCmd = "CQI_CL_STRUC2CPOS($attribute, $struc)";
488 0           cqi_send_word($CWB::CQI::CL_STRUC2CPOS);
489 0           cqi_send_string($attribute);
490 0           cqi_send_int($struc);
491 0           cqi_flush();
492 0           return cqi_expect_int_int();
493             }
494              
495             sub cqi_alg2cpos {
496 0 0 0 0 0   croak "USAGE: (\$s1, \$s2, \$t1, \$t2) = cqi_alg2cpos(\$attribute, \$alg);"
497             unless @_ == 2 and wantarray;
498 0           my $attribute = shift;
499 0           my $alg = shift;
500              
501 0           $LastCmd = "CQI_CL_ALG2CPOS($attribute, $alg)";
502 0           cqi_send_word($CWB::CQI::CL_ALG2CPOS);
503 0           cqi_send_string($attribute);
504 0           cqi_send_int($alg);
505 0           cqi_flush();
506 0           return cqi_expect_int_int_int_int();
507             }
508              
509             # cqi_query() returns a CQi response code (CQI_STATUS_OK or error).
510             # An error code usually indicates a mistake in the query syntax.
511             # It aborts the program unless one of the following responses is received:
512             # CQI_STATUS_OK
513             # CQI_ERROR_*
514             # CQI_CQP_ERROR_*
515             sub cqi_query {
516 0     0 0   my ($mother, $child, $query) = @_;
517 0 0 0       croak "USAGE: \$ok = cqi_query(\$mother_corpus, \$subcorpus_name, \$query);"
      0        
518             unless @_ == 3 and $mother =~ /^[A-Z0-9_-]+(:[A-Z_][A-Za-z0-9_-]*)?$/
519             and $child =~ /^[A-Z_][A-Za-z0-9_-]*$/;
520 0 0         $query .= ";"
521             unless $query =~ /;\s*$/;
522            
523 0           $LastCmd = "CQI_CQP_QUERY($mother, $child, '$query')";
524 0           cqi_send_word($CWB::CQI::CQP_QUERY);
525 0           cqi_send_string($mother);
526 0           cqi_send_string($child);
527 0           cqi_send_string($query);
528 0           cqi_flush();
529 0           my $response = cqi_read_word();
530 0           my $group = $response >> 8;
531 0 0 0       CqiError $response
      0        
532             unless $response == $CWB::CQI::STATUS_OK or $group == $CWB::CQI::ERROR or $group == $CWB::CQI::CQP_ERROR;
533 0           return $response;
534             }
535              
536             sub cqi_list_subcorpora {
537 0     0 0   my $corpus = shift;
538 0           $LastCmd = "CQI_CQP_LIST_SUBCORPORA($corpus)";
539 0           cqi_send_word($CWB::CQI::CQP_LIST_SUBCORPORA);
540 0           cqi_send_string($corpus);
541 0           cqi_flush();
542 0           return cqi_expect_string_list();
543             }
544              
545             sub cqi_subcorpus_size {
546 0     0 0   my $subcorpus = shift;
547 0           $LastCmd = "CQI_CQP_SUBCORPUS_SIZE($subcorpus)";
548 0           cqi_send_word($CWB::CQI::CQP_SUBCORPUS_SIZE);
549 0           cqi_send_string($subcorpus);
550 0           cqi_flush();
551 0           return cqi_expect_int();
552             }
553              
554             # used internally
555             sub cqi_get_field_key {
556 0     0 0   my $field = uc shift;
557 0 0         if ($field =~ /^(MATCH(END)?|TARGET|KEYWORD)$/) {
558 0           return eval "\$CWB::CQI::CONST_FIELD_$field";
559             }
560             else {
561 0           return undef;
562             }
563             }
564              
565             sub cqi_subcorpus_has_field {
566 0     0 0   my ($subcorpus, $field) = @_;
567 0 0 0       croak "USAGE: \$ok = cqi_subcorpus_has_field(\$subcorpus, 'match'|'matchend'|'target'|'keyword');"
568             unless @_ == 2 and defined (my $field_key = cqi_get_field_key($field));
569 0           $LastCmd = "CQI_CQP_SUBCORPUS_HAS_FIELD($subcorpus, CQI_CONST_FIELD_".(uc $field).")";
570 0           cqi_send_word($CWB::CQI::CQP_SUBCORPUS_HAS_FIELD);
571 0           cqi_send_string($subcorpus);
572 0           cqi_send_byte($field_key);
573 0           cqi_flush();
574 0           return cqi_expect_bool();
575             }
576              
577             sub cqi_dump_subcorpus {
578 0     0 0   my ($subcorpus, $field, $first, $last) = @_;
579 0 0 0       croak "USAGE: \@column = cqi_dump_subcorpus(\$subcorpus, 'match'|'matchend'|'target'|'keyword', \$from, \$to);"
580             unless @_ == 4 and defined (my $field_key = cqi_get_field_key($field));
581 0           $LastCmd = "CQI_CQP_DUMP_SUBCORPUS($subcorpus, CQI_CONST_FIELD_".(uc $field).", $first, $last)";
582 0           cqi_send_word($CWB::CQI::CQP_DUMP_SUBCORPUS);
583 0           cqi_send_string($subcorpus);
584 0           cqi_send_byte($field_key);
585 0           cqi_send_int($first);
586 0           cqi_send_int($last);
587 0           cqi_flush();
588 0           return cqi_expect_int_list();
589             }
590              
591             sub cqi_drop_subcorpus {
592 0     0 0   my $subcorpus = shift;
593 0           $LastCmd = "CQI_CQP_DROP_SUBCORPUS($subcorpus)";
594 0           cqi_send_word($CWB::CQI::CQP_DROP_SUBCORPUS);
595 0           cqi_send_string($subcorpus);
596 0           cqi_flush();
597 0           cqi_expect_status($CWB::CQI::STATUS_OK);
598             }
599              
600             ## cqi_fdist() subsumes both cqi_fdist_1() and cqi_fdist_2()
601             ## returns list of (id, f) or (id1, id2, f) tuples as hashref's
602             sub cqi_fdist {
603 0     0 0   my $subcorpus = shift;
604 0           my $cutoff = shift;
605 0           my $key1 = shift;
606 0           my $key2 = shift;
607 0           my ($field1, $field2, $att1, $att2, $tmp);
608 0           ($tmp, $att1) = split /\./, $key1;
609 0           $field1 = cqi_get_field_key($tmp);
610 0 0         if (defined $key2) {
611 0           ($tmp, $att2) = split /\./, $key2;
612 0           $field2 = cqi_get_field_key($tmp);
613             }
614             else {
615 0           $field2 = "";
616 0           $att2 = "x";
617             }
618 0 0 0       croak "USAGE: \@table = cqi_fdist(\$subcorpus, \$cutoff, \$key1 [, \$key2]);"
      0        
      0        
      0        
      0        
      0        
      0        
619             unless @_ == 0 and defined $field1 and defined $field2 and defined $att1 and defined $att2
620             and $att1 =~ /^[a-z]+$/ and $att2 =~ /^[a-z]+$/ and $cutoff >= 0;
621 0 0         if ($field2 ne "") {
622 0           $LastCmd = "CQI_CQP_FDIST_2($subcorpus, $cutoff, $key1, $key2)";
623 0           cqi_send_word($CWB::CQI::CQP_FDIST_2);
624 0           cqi_send_string($subcorpus);
625 0           cqi_send_int($cutoff);
626 0           cqi_send_byte($field1);
627 0           cqi_send_string($att1);
628 0           cqi_send_byte($field2);
629 0           cqi_send_string($att2);
630 0           cqi_flush();
631 0           return cqi_expect_int_table();
632             }
633             else {
634 0           $LastCmd = "CQI_CQP_FDIST_1($subcorpus, $cutoff, $key1)";
635 0           cqi_send_word($CWB::CQI::CQP_FDIST_1);
636 0           cqi_send_string($subcorpus);
637 0           cqi_send_int($cutoff);
638 0           cqi_send_byte($field1);
639 0           cqi_send_string($att1);
640 0           cqi_flush();
641 0           return cqi_expect_int_table();
642             }
643             }
644              
645              
646             #
647             #
648             # CQi expect response / data
649             #
650             #
651             sub cqi_expect_byte {
652 0     0 0   my $r = cqi_read_word();
653 0           CqiCheckResponse $r, $CWB::CQI::DATA_BYTE;
654 0           return cqi_read_byte();
655             }
656              
657             sub cqi_expect_bool {
658 0     0 0   my $r = cqi_read_word();
659 0           CqiCheckResponse $r, $CWB::CQI::DATA_BOOL;
660 0           return cqi_read_byte();
661             }
662              
663             sub cqi_expect_int {
664 0     0 0   my $r = cqi_read_word();
665 0           CqiCheckResponse $r, $CWB::CQI::DATA_INT;
666 0           return cqi_read_int();
667             }
668              
669             sub cqi_expect_string {
670 0     0 0   my $r = cqi_read_word();
671 0           CqiCheckResponse $r, $CWB::CQI::DATA_STRING;
672 0           return cqi_read_string();
673             }
674              
675             sub cqi_expect_byte_list {
676 0     0 0   my $r = cqi_read_word();
677 0           CqiCheckResponse $r, $CWB::CQI::DATA_BYTE_LIST;
678 0           return cqi_read_byte_list();
679             }
680              
681             sub cqi_expect_bool_list {
682 0     0 0   my $r = cqi_read_word();
683 0           CqiCheckResponse $r, $CWB::CQI::DATA_BOOL_LIST;
684 0           return cqi_read_byte_list();
685             }
686              
687             sub cqi_expect_int_list {
688 0     0 0   my $r = cqi_read_word();
689 0           CqiCheckResponse $r, $CWB::CQI::DATA_INT_LIST;
690 0           return cqi_read_int_list();
691             }
692              
693             sub cqi_expect_string_list {
694 0     0 0   my $r = cqi_read_word();
695 0           CqiCheckResponse $r, $CWB::CQI::DATA_STRING_LIST;
696 0           return cqi_read_string_list();
697             }
698              
699             sub cqi_expect_int_int {
700 0     0 0   my $r = cqi_read_word();
701 0           CqiCheckResponse $r, $CWB::CQI::DATA_INT_INT;
702 0           return cqi_read_int(), cqi_read_int();
703             }
704              
705             sub cqi_expect_int_int_int_int {
706 0     0 0   my $r = cqi_read_word();
707 0           CqiCheckResponse $r, $CWB::CQI::DATA_INT_INT_INT_INT;
708 0           return cqi_read_int(), cqi_read_int(), cqi_read_int(), cqi_read_int();
709             }
710              
711             sub cqi_expect_int_table {
712 0     0 0   my $r = cqi_read_word();
713 0           CqiCheckResponse $r, $CWB::CQI::DATA_INT_TABLE;
714 0           return cqi_read_int_table();
715             }
716              
717             sub cqi_expect_status {
718 0     0 0   my @expected = @_; # arguments are list of acceptable responses
719 0           my $r = cqi_read_word();
720 0           CqiCheckResponse $r, @expected;
721 0           return $r;
722             }
723              
724              
725             #
726             #
727             # Internal subroutines (read / write)
728             #
729             #
730             sub cqi_send_byte ($) {
731 0 0   0 0   $conn->print((pack "C", shift))
732             or croak "cqi_send_byte(): $!";
733             }
734              
735             sub cqi_send_word ($) {
736 0 0   0 0   $conn->print((pack "n", shift))
737             or croak "cqi_send_word(): $!";
738             }
739              
740             sub cqi_send_int ($) {
741 0     0 0   my $number = shift; # safely convert native int to 32bit value
742 0           $number = unpack "L", (pack "l", $number); # pack 32bit signed, unpack unsigned -> uses type which can hold unsigned 32bit value
743 0 0         $conn->print(pack("N", $number)) # 'N' packs unsigned 32bit integer
744             or croak "cqi_send_int(): $!";
745             }
746              
747             sub cqi_send_string ($) {
748 0     0 0   my $str = shift;
749 0 0         $conn->print((pack "n", length $str), $str)
750             or croak "cqi_send_str(): $!";
751             }
752              
753             sub cqi_send_byte_list (@) {
754 0     0 0   cqi_send_int(scalar @_);
755 0           map {cqi_send_byte($_)} @_;
  0            
756             }
757              
758             sub cqi_send_word_list (@) {
759 0     0 0   cqi_send_int(scalar @_);
760 0           map {cqi_send_word($_)} @_;
  0            
761             }
762              
763             sub cqi_send_int_list (@) {
764 0     0 0   cqi_send_int(scalar @_);
765 0           map {cqi_send_int($_)} @_;
  0            
766             }
767              
768             sub cqi_send_string_list (@) {
769 0     0 0   cqi_send_int(scalar @_);
770 0           map {cqi_send_string($_)} @_;
  0            
771             }
772              
773             sub cqi_flush () {
774 0 0   0 0   $conn->flush
775             or croak "cqi_flush(): $!";
776             }
777              
778             sub cqi_read_byte () {
779 0     0 0   my $msg = $conn->getc();
780 0 0         croak "cqi_read_byte(): $!"
781             unless defined $msg;
782 0           return unpack "C", $msg;
783             }
784              
785             sub cqi_read_word () {
786 0     0 0   my $msg;
787 0           my $bytes_read = $conn->read($msg, 2);
788 0 0 0       croak "cqi_read_word(): $!"
789             unless defined $bytes_read and $bytes_read == 2;
790 0           return unpack "N", "\x00\x00$msg"; # this should safely unpack an unsigned short
791             }
792              
793             sub cqi_read_int () {
794 0     0 0   my $msg;
795             my $number;
796            
797 0           my $bytes_read = $conn->read($msg, 4);
798 0 0 0       croak "cqi_read_word(): $!"
799             unless defined $bytes_read and $bytes_read == 4;
800 0           $number = unpack "N", $msg; # unpack seems to default to unsigned
801 0           $number = unpack "l", (pack "L", $number); # convert unsigned 32bit to internal signed int *phew*
802 0           return $number;
803             }
804              
805             sub cqi_read_string () {
806 0     0 0   my ($msg, $len, $bytes_read);
807 0           $len = cqi_read_word();
808 0           $bytes_read = $conn->read($msg, $len);
809 0 0 0       croak "cqi_read_string(): $!"
810             unless defined $bytes_read and $bytes_read == $len;
811 0           return $msg;
812             }
813              
814             sub cqi_read_byte_list() {
815 0     0 0   my ($i, $len, @list);
816 0           $len = cqi_read_int();
817 0           for ($i = $len; $i > 0; $i--) {
818 0           push @list, cqi_read_byte;
819             }
820 0           return @list;
821             }
822              
823             sub cqi_read_word_list() {
824 0     0 0   my ($i, $len, @list);
825 0           $len = cqi_read_int();
826 0           for ($i = $len; $i > 0; $i--) {
827 0           push @list, cqi_read_word();
828             }
829 0           return @list;
830             }
831              
832             sub cqi_read_int_list() {
833 0     0 0   my ($i, $len, @list);
834 0           $len = cqi_read_int();
835 0           for ($i = $len; $i > 0; $i--) {
836 0           push @list, cqi_read_int();
837             }
838 0           return @list;
839             }
840              
841             sub cqi_read_string_list() {
842 0     0 0   my ($i, $len, @list);
843 0           $len = cqi_read_int();
844 0           for ($i = $len; $i > 0; $i--) {
845 0           push @list, cqi_read_string();
846             }
847 0           return @list;
848             }
849              
850             sub cqi_read_int_table() {
851 0     0 0   my $rows = cqi_read_int();
852 0           my $columns = cqi_read_int();
853 0           my @table = ();
854 0           for (my $i = 0; $i < $rows; $i++) {
855 0           my @line = ();
856 0           for (my $j = 0; $j < $columns; $j++) {
857 0           push @line, cqi_read_int();
858             }
859 0           push @table, [@line];
860             }
861 0           return @table;
862             }
863              
864              
865             1;
866              
867             __END__