1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME : message.c
3 @DESCRIPTION: Routines for doing acr_nema message operations.
4 @METHOD :
5 @GLOBALS :
6 @CREATED : November 16, 1993 (Peter Neelin)
7 @MODIFIED :
8 * $Log: message.c,v $
9 * Revision 6.8 2008-08-12 05:00:22 rotor
10 * * large number of changes from Claude (64 bit and updates)
11 *
12 * Revision 6.7 2004/10/29 13:08:42 rotor
13 * * rewrote Makefile with no dependency on a minc distribution
14 * * removed all references to the abominable minc_def.h
15 * * I should autoconf this really, but this is old code that
16 * is now replaced by Jon Harlaps PERL version..
17 *
18 * Revision 6.6 2002/12/08 21:43:08 neelin
19 * Fixed excessive memory freeing on error when reading message (seen in linux)
20 *
21 * Revision 6.5 2002/11/13 03:00:27 neelin
22 * Fixed an unterminated comment
23 *
24 * Revision 6.4 1999/10/29 17:51:53 neelin
25 * Fixed Log keyword
26 *
27 * Revision 6.3 1998/03/10 17:06:29 neelin
28 * Added code to acr_input_message so that if we reach the watchpoint and
29 * more message is expected, we keep on reading.
30 *
31 * Revision 6.2 1998/03/09 19:30:23 neelin
32 * Fixed bug in acr_input_message where the last group added to the input
33 * message was being deleted followed by the message itself when a
34 * message length error occurred. When an input error occurs the message
35 * should not be deleted.
36 *
37 * Revision 6.1 1998/02/18 20:27:13 neelin
38 * Minor bug fix.
39 *
40 * Revision 6.0 1997/09/12 13:23:59 neelin
41 * Release of minc version 0.6
42 *
43 * Revision 5.0 1997/08/21 13:25:00 neelin
44 * Release of minc version 0.5
45 *
46 * Revision 4.1 1997/06/13 21:27:16 neelin
47 * Made use of message length to figure out how much to read in -
48 * previously was not checking it, so if watchpoint was not set, we would
49 * read indefinitely.
50 *
51 * Revision 4.0 1997/05/07 20:01:23 neelin
52 * Release of minc version 0.4
53 *
54 * Revision 3.1 1997/04/21 20:21:09 neelin
55 * Updated the library to handle dicom messages.
56 *
57 * Revision 3.0 1995/05/15 19:32:12 neelin
58 * Release of minc version 0.3
59 *
60 * Revision 2.0 1994/09/28 10:36:17 neelin
61 * Release of minc version 0.2
62 *
63 * Revision 1.6 94/09/28 10:35:49 neelin
64 * Pre-release
65 *
66 * Revision 1.5 94/05/18 08:48:12 neelin
67 * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT.
68 *
69 * Revision 1.4 94/04/07 10:05:06 neelin
70 * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs
71 * to that or ACR_OTHER_ERROR.
72 * Added #ifdef lint to DEFINE_ELEMENT.
73 *
74 * Revision 1.3 93/11/24 11:25:59 neelin
75 * Added dump_message.
76 *
77 * Revision 1.2 93/11/22 13:12:09 neelin
78 * Changed to use new Acr_Element_Id stuff
79 *
80 * Revision 1.1 93/11/19 12:49:09 neelin
81 * Initial revision
82 *
83 @COPYRIGHT :
84 Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre,
85 Montreal Neurological Institute, McGill University.
86 Permission to use, copy, modify, and distribute this
87 software and its documentation for any purpose and without
88 fee is hereby granted, provided that the above copyright
89 notice appear in all copies. The author and McGill University
90 make no representations about the suitability of this
91 software for any purpose. It is provided "as is" without
92 express or implied warranty.
93 ---------------------------------------------------------------------------- */
94
95 #include <stdlib.h>
96 #include <stdio.h>
97 #include <limits.h>
98 #include <acr_nema.h>
99
100 /* Message length group and element id */
101 #define ACR_GID_MESSLEN 0
102 #define ACR_EID_MESSLEN 1
103
104 DEFINE_ELEMENT(static, ACR_Message_length,
105 ACR_GID_MESSLEN, ACR_EID_MESSLEN, UL);
106
107 /* Private functions */
108 private void update_message_length_element(Acr_Message message,
109 Acr_VR_encoding_type vr_encoding);
110
111 /* ----------------------------- MNI Header -----------------------------------
112 @NAME : acr_create_message
113 @INPUT : (none)
114 @OUTPUT : (none)
115 @RETURNS : Pointer to message structure
116 @DESCRIPTION: Creates an acr-nema message structure.
117 @METHOD : No groups are created here. Message length is checked when
118 groups are added.
119 @GLOBALS :
120 @CALLS :
121 @CREATED : November 16, 1993 (Peter Neelin)
122 @MODIFIED :
123 ---------------------------------------------------------------------------- */
acr_create_message(void)124 public Acr_Message acr_create_message(void)
125 {
126 Acr_Message message;
127
128 /* Allocate the message */
129 message = MALLOC(sizeof(*message));
130
131 /* Assign fields */
132 acr_message_reset(message);
133
134 return message;
135 }
136
137 /* ----------------------------- MNI Header -----------------------------------
138 @NAME : acr_delete_message
139 @INPUT : message
140 @OUTPUT : (none)
141 @RETURNS : (nothing)
142 @DESCRIPTION: Deletes an acr-nema message structure (freeing the group list)
143 @METHOD :
144 @GLOBALS :
145 @CALLS :
146 @CREATED : November 16, 1993 (Peter Neelin)
147 @MODIFIED :
148 ---------------------------------------------------------------------------- */
acr_delete_message(Acr_Message message)149 public void acr_delete_message(Acr_Message message)
150 {
151 if (message->list_head != NULL) {
152 acr_delete_group_list(message->list_head);
153 }
154
155 FREE(message);
156
157 return;
158 }
159
160 /* ----------------------------- MNI Header -----------------------------------
161 @NAME : acr_message_reset
162 @INPUT : message
163 @OUTPUT : (none)
164 @RETURNS : (nothing)
165 @DESCRIPTION: Resets the message to empty without freeing any existing
166 group list. Thus the group list can be used elsewhere
167 without being deleted when the message is deleted.
168 @METHOD :
169 @GLOBALS :
170 @CALLS :
171 @CREATED : February 18, 1997 (Peter Neelin)
172 @MODIFIED :
173 ---------------------------------------------------------------------------- */
acr_message_reset(Acr_Message message)174 public void acr_message_reset(Acr_Message message)
175 {
176 message->ngroups = 0;
177 message->implicit_total_length = 0;
178 message->explicit_total_length = 0;
179 message->message_implicit_offset = 0;
180 message->message_explicit_offset = 0;
181 message->message_length_element = NULL;
182 message->list_head = NULL;
183 message->list_tail = NULL;
184 }
185
186 /* ----------------------------- MNI Header -----------------------------------
187 @NAME : acr_message_add_group
188 @INPUT : message
189 group
190 @OUTPUT : (none)
191 @RETURNS : (nothing)
192 @DESCRIPTION: Add a group to an acr-nema message
193 @METHOD : If this is the first group, then look for a message length
194 element. If none is found, then report an error.
195 @GLOBALS :
196 @CALLS :
197 @CREATED : November 16, 1993 (Peter Neelin)
198 @MODIFIED : February 7, 1997 (P.N.)
199 ---------------------------------------------------------------------------- */
acr_message_add_group(Acr_Message message,Acr_Group group)200 public void acr_message_add_group(Acr_Message message, Acr_Group group)
201 {
202 Acr_Element length_element;
203 int element_id;
204 long message_implicit_offset, message_explicit_offset;
205
206 /* If first message, then check for message length element */
207 if (message->ngroups == 0) {
208 length_element = NULL;
209 message_implicit_offset = 0;
210 message_explicit_offset = 0;
211 if (acr_get_group_group(group) == ACR_GID_MESSLEN) {
212 length_element = acr_get_group_element_list(group);
213 if (length_element != NULL) {
214 element_id = acr_get_element_element(length_element);
215 message_implicit_offset =
216 acr_get_element_total_length(length_element, ACR_IMPLICIT_VR);
217 message_explicit_offset =
218 acr_get_element_total_length(length_element, ACR_EXPLICIT_VR);
219 }
220 else {
221 element_id = 0;
222 message_implicit_offset = 0;
223 message_explicit_offset = 0;
224 }
225 while ((element_id != ACR_EID_MESSLEN) &&
226 (length_element != NULL)) {
227 length_element = acr_get_element_next(length_element);
228 if (length_element != NULL) {
229 element_id = acr_get_element_element(length_element);
230 message_implicit_offset +=
231 acr_get_element_total_length(length_element,
232 ACR_IMPLICIT_VR);
233 message_explicit_offset +=
234 acr_get_element_total_length(length_element,
235 ACR_EXPLICIT_VR);
236 }
237 }
238 }
239
240 /* Check for length element found but not of correct length */
241 if ((length_element != NULL) &&
242 (acr_get_element_length(length_element) != ACR_SIZEOF_LONG)) {
243 (void) fprintf(stderr,
244 "ACR error: First message group contains length element of wrong size\n");
245 exit(EXIT_FAILURE);
246 }
247
248 /* Set up the message length info */
249 message->message_implicit_offset = message_implicit_offset;
250 message->message_explicit_offset = message_explicit_offset;
251 message->message_length_element = length_element;
252
253 }
254
255 /* Add group (check for empty list) */
256 if (message->ngroups == 0) {
257 message->list_head = group;
258 message->list_tail = group;
259 }
260 else {
261 acr_set_group_next(message->list_tail, group);
262 message->list_tail = group;
263 acr_set_group_next(group, NULL);
264 }
265 message->ngroups++;
266 message->implicit_total_length +=
267 acr_get_group_total_length(group, ACR_IMPLICIT_VR);
268 message->explicit_total_length +=
269 acr_get_group_total_length(group, ACR_EXPLICIT_VR);
270
271 /* Update the length element */
272 if (message->message_length_element != NULL) {
273 update_message_length_element(message,
274 acr_get_element_vr_encoding(message->message_length_element));
275 }
276
277 return;
278 }
279
280 /* ----------------------------- MNI Header -----------------------------------
281 @NAME : update_message_length_element
282 @INPUT : message
283 vr_encoding - ACR_IMPLICIT_VR or ACR_EXPLICIT_VR
284 @OUTPUT : (none)
285 @RETURNS : (nothing)
286 @DESCRIPTION: Update the length element of the message according to the VR type
287 @METHOD :
288 @GLOBALS :
289 @CALLS :
290 @CREATED : February 14, 1997 (Peter Neelin)
291 @MODIFIED :
292 ---------------------------------------------------------------------------- */
update_message_length_element(Acr_Message message,Acr_VR_encoding_type vr_encoding)293 private void update_message_length_element(Acr_Message message,
294 Acr_VR_encoding_type vr_encoding)
295 {
296 Acr_Long message_length;
297 Acr_Element length_element;
298 void *message_length_data;
299
300 /* Get the element */
301 length_element = message->message_length_element;
302 if (length_element == NULL) return;
303
304 /* Calculate the appropriate length */
305 if (vr_encoding == ACR_IMPLICIT_VR) {
306 message_length = message->implicit_total_length -
307 message->message_implicit_offset;
308 }
309 else {
310 message_length = message->explicit_total_length -
311 message->message_explicit_offset;
312 }
313
314 /* Update the element */
315 message_length_data = acr_get_element_data(length_element);
316 acr_put_long(acr_get_element_byte_order(length_element),
317 1, &message_length, message_length_data);
318
319 }
320
321 /* ----------------------------- MNI Header -----------------------------------
322 @NAME : acr_message_add_group_list
323 @INPUT : message
324 group_list
325 @OUTPUT : (none)
326 @RETURNS : (nothing)
327 @DESCRIPTION: Add a group list to an acr-nema message
328 @METHOD :
329 @GLOBALS :
330 @CALLS :
331 @CREATED : February 12, 1997 (Peter Neelin)
332 @MODIFIED :
333 ---------------------------------------------------------------------------- */
acr_message_add_group_list(Acr_Message message,Acr_Group group_list)334 public void acr_message_add_group_list(Acr_Message message,
335 Acr_Group group_list)
336 {
337 Acr_Group next_group, group;
338
339 /* Loop through groups, adding them to the message */
340 group = group_list;
341 while (group != NULL) {
342 next_group = acr_get_group_next(group);
343 acr_set_group_next(group, NULL);
344 acr_message_add_group(message, group);
345 group = next_group;
346 }
347
348 }
349
350 /* ----------------------------- MNI Header -----------------------------------
351 @NAME : acr_get_message_group_list
352 @INPUT : message
353 @OUTPUT : (none)
354 @RETURNS : group list
355 @DESCRIPTION: Get group list for message
356 @METHOD :
357 @GLOBALS :
358 @CALLS :
359 @CREATED : November 16, 1993 (Peter Neelin)
360 @MODIFIED :
361 ---------------------------------------------------------------------------- */
acr_get_message_group_list(Acr_Message message)362 public Acr_Group acr_get_message_group_list(Acr_Message message)
363 {
364 return message->list_head;
365 }
366
367 /* ----------------------------- MNI Header -----------------------------------
368 @NAME : acr_get_message_total_length
369 @INPUT : message
370 vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR
371 @OUTPUT : (none)
372 @RETURNS : total length of message
373 @DESCRIPTION: Get total length of message depending on type of VR encoding
374 @METHOD :
375 @GLOBALS :
376 @CALLS :
377 @CREATED : November 16, 1993 (Peter Neelin)
378 @MODIFIED :
379 ---------------------------------------------------------------------------- */
acr_get_message_total_length(Acr_Message message,Acr_VR_encoding_type vr_encoding)380 public long acr_get_message_total_length(Acr_Message message,
381 Acr_VR_encoding_type vr_encoding)
382 {
383 if (vr_encoding == ACR_IMPLICIT_VR)
384 return message->implicit_total_length;
385 else
386 return message->explicit_total_length;
387 }
388
389 /* ----------------------------- MNI Header -----------------------------------
390 @NAME : acr_get_message_ngroups
391 @INPUT : message
392 @OUTPUT : (none)
393 @RETURNS : number of groups in message
394 @DESCRIPTION: Get number of groups in message
395 @METHOD :
396 @GLOBALS :
397 @CALLS :
398 @CREATED : November 16, 1993 (Peter Neelin)
399 @MODIFIED :
400 ---------------------------------------------------------------------------- */
acr_get_message_ngroups(Acr_Message message)401 public int acr_get_message_ngroups(Acr_Message message)
402 {
403 return message->ngroups;
404 }
405
406 /* ----------------------------- MNI Header -----------------------------------
407 @NAME : acr_input_message
408 @INPUT : afp - acr file pointer
409 @OUTPUT : message
410 @RETURNS : status
411 @DESCRIPTION: Read in an acr-nema message. The amount of input to read is
412 determined either by setting a watchpoint or by having messages
413 that contain their own length. One cannot rely on finding an
414 EOF (as for a file) since messages usually come over a
415 connection.
416 @METHOD :
417 @GLOBALS :
418 @CALLS :
419 @CREATED : November 16, 1993 (Peter Neelin)
420 @MODIFIED : February 7, 1997 (P.N.)
421 ---------------------------------------------------------------------------- */
acr_input_message(Acr_File * afp,Acr_Message * message)422 public Acr_Status acr_input_message(Acr_File *afp, Acr_Message *message)
423 {
424 Acr_Status status;
425 Acr_Group group;
426 Acr_Element length_element;
427 Acr_Long lvalue;
428 long message_length;
429 long group_length;
430 long watchpoint;
431 int get_more_groups;
432
433 /* Initialize the message pointer */
434 *message = NULL;
435
436 /* Read in the first group */
437 status = acr_input_group(afp, &group);
438
439 /* Check status */
440 if (status != ACR_OK) {
441 if (group != NULL) acr_delete_group(group);
442 return status;
443 }
444
445 /* Check whether it contains the message length. If there is no
446 watchpoint, then it must contain the length, otherwise we don't
447 know when to stop reading (we would if the input was from a file, but
448 messages don't usually come from a file) */
449 length_element =
450 acr_find_group_element(group, ACR_Message_length);
451 if ((acr_get_io_watchpoint(afp) == ACR_NO_WATCHPOINT) &&
452 ((length_element == NULL) ||
453 (acr_get_element_length(length_element) != ACR_SIZEOF_LONG))) {
454 acr_delete_group(group);
455 status = ACR_PROTOCOL_ERROR;
456 return status;
457 }
458
459 /* Get the message length from the element */
460 if (length_element != NULL) {
461 acr_get_long(acr_get_element_byte_order(length_element), (long) 1,
462 acr_get_element_data(length_element), &lvalue);
463 message_length = (long)lvalue;
464 }
465
466 /* Create the message and add the group (this will modify the message
467 length element value to include only the first group) */
468 *message = acr_create_message();
469 acr_message_add_group(*message, group);
470
471 /* Correct message_length for the length of the first group. Note
472 that adding the group had the side effect of changing the
473 message length to include only the first group */
474 if (length_element != NULL) {
475 acr_get_long(acr_get_element_byte_order(length_element), (long) 1,
476 acr_get_element_data(length_element), &lvalue);
477 group_length = (long)lvalue;
478 message_length -= group_length;
479 }
480
481 /* Loop through elements, adding them to the list */
482 get_more_groups = ((length_element != NULL) ? (message_length > 0) : TRUE);
483 while (get_more_groups) {
484
485 /* Check for a watchpoint. If we have reached it, but the message
486 length indicates that there is more to come, then move it along
487 so that we can keep reading. */
488 watchpoint = acr_get_io_watchpoint(afp);
489 if ((watchpoint == 0) &&
490 (length_element != NULL) && (message_length > 0)) {
491 acr_set_io_watchpoint(afp, LONG_MAX-1);
492 }
493 else if (watchpoint <= 0) {
494 get_more_groups = FALSE;
495 break;
496 }
497
498 /* If we reach the end of a fragment and PDU, but we need more data */
499
500 /* Read in the next group and check for an error */
501 status = acr_input_group(afp, &group);
502
503 /* Add the group to the message */
504 if (group != NULL) {
505 acr_message_add_group(*message, group);
506 }
507
508 /* Check the status */
509 if (status != ACR_OK) {
510 if (status == ACR_END_OF_INPUT) status = ACR_ABNORMAL_END_OF_INPUT;
511 return status;
512 }
513
514 /* Keep track of remaining bytes to read, if necessary */
515 if (length_element != NULL) {
516 message_length -=
517 acr_get_group_total_length(group, acr_get_vr_encoding(afp));
518 get_more_groups = (message_length > 0);
519 }
520 }
521
522 /* Check that we got a full message */
523 if ((length_element != NULL) && (message_length != 0)) {
524 status = ACR_PROTOCOL_ERROR;
525 return status;
526 }
527
528 return status;
529 }
530
531 /* ----------------------------- MNI Header -----------------------------------
532 @NAME : acr_output_message
533 @INPUT : afp - acr file pointer
534 message
535 @OUTPUT : (none)
536 @RETURNS : status
537 @DESCRIPTION: Write out an acr-nema message
538 @METHOD :
539 @GLOBALS :
540 @CALLS :
541 @CREATED : November 16, 1993 (Peter Neelin)
542 @MODIFIED :
543 ---------------------------------------------------------------------------- */
acr_output_message(Acr_File * afp,Acr_Message message)544 public Acr_Status acr_output_message(Acr_File *afp, Acr_Message message)
545 {
546 long igroup, ngroups;
547 Acr_Group cur, next;
548 Acr_Status status;
549
550 /* Update the length element */
551 update_message_length_element(message, acr_get_vr_encoding(afp));
552
553 /* Loop through the groups of the message, writing them out */
554 ngroups = acr_get_message_ngroups(message);
555 next = acr_get_message_group_list(message);
556 for (igroup=0; igroup < ngroups && next != NULL; igroup++) {
557 cur = next;
558 next = cur->next;
559 status = acr_output_group(afp, cur);
560 if (status != ACR_OK) {
561 return status;
562 }
563 }
564
565 /* Flush the buffer */
566 if (acr_file_flush(afp) == EOF) {
567 status = ACR_ABNORMAL_END_OF_OUTPUT;
568 return status;
569 }
570
571 /* Check for a bogus message (the true number of groups is different from
572 ngroups) */
573 if ((igroup < ngroups) || (next != NULL)) {
574 status = ACR_OTHER_ERROR;
575 return status;
576 }
577
578 return status;
579
580 }
581
582 /* ----------------------------- MNI Header -----------------------------------
583 @NAME : acr_dump_message
584 @INPUT : file_pointer - where output should go
585 group_list
586 @OUTPUT : (none)
587 @RETURNS : (nothing)
588 @DESCRIPTION: Dump information from an acr-nema message
589 @METHOD :
590 @GLOBALS :
591 @CALLS :
592 @CREATED : November 24, 1993 (Peter Neelin)
593 @MODIFIED :
594 ---------------------------------------------------------------------------- */
acr_dump_message(FILE * file_pointer,Acr_Message message)595 public void acr_dump_message(FILE *file_pointer, Acr_Message message)
596 {
597
598 acr_dump_group_list(file_pointer, acr_get_message_group_list(message));
599
600 return;
601 }
602