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