1 /* ----------------------------- MNI Header -----------------------------------
2 @NAME       : acr_io.c
3 @DESCRIPTION: Routines for doing basic acr_nema operations (reading and
4               writing an element).
5 @METHOD     :
6 @GLOBALS    :
7 @CREATED    : November 10, 1993 (Peter Neelin)
8 @MODIFIED   :
9  * $Log: acr_io.c,v $
10  * Revision 6.8  2008-08-12 05:00:22  rotor
11  *  * large number of changes from Claude (64 bit and updates)
12  *
13  * Revision 6.7  2005/03/04 00:15:14  bert
14  * Lose public and private keywords; change acr_read_one_element to assume implicit VR format for special sequence delimiters (group 0xfffe)
15  *
16  * Revision 6.6  2004/10/29 13:08:41  rotor
17  *  * rewrote Makefile with no dependency on a minc distribution
18  *  * removed all references to the abominable minc_def.h
19  *  * I should autoconf this really, but this is old code that
20  *      is now replaced by Jon Harlaps PERL version..
21  *
22  * Revision 6.5  2000/08/16 15:53:46  neelin
23  * Added VR type UN (unknown) which has a length field similar to OB.
24  *
25  * Revision 6.4  2000/05/01 17:54:02  neelin
26  * Improved testing of input stream to figure out byte order for both
27  * implicit and expicit VR.
28  *
29  * Revision 6.3  2000/04/28 15:03:10  neelin
30  * Added support for ignoring non-fatal protocol errors (cases where redundant
31  * information is inconsistent). In particular, it is possible to ignore
32  * differences between the group length element and the true group length.
33  *
34  * Revision 6.2  1999/10/29 17:51:49  neelin
35  * Fixed Log keyword
36  *
37  * Revision 6.1  1999/10/27 20:13:15  neelin
38  * Generalized acr_test_byte_order to recognize groups without a length element.
39  *
40  * Revision 6.0  1997/09/12 13:23:59  neelin
41  * Release of minc version 0.6
42  *
43  * Revision 5.1  1997/09/08  21:53:31  neelin
44  * Added status ACR_CONNECTION_TIMEDOUT.
45  *
46  * Revision 5.0  1997/08/21  13:25:00  neelin
47  * Release of minc version 0.5
48  *
49  * Revision 4.1  1997/07/10  17:14:38  neelin
50  * Added more status codes and function to return status string.
51  *
52  * Revision 4.0  1997/05/07  20:01:23  neelin
53  * Release of minc version 0.4
54  *
55  * Revision 3.1  1997/04/21  20:21:09  neelin
56  * Updated the library to handle dicom messages.
57  *
58  * Revision 3.0  1995/05/15  19:32:12  neelin
59  * Release of minc version 0.3
60  *
61  * Revision 2.1  1995/02/08  21:16:06  neelin
62  * Changes to make irix 5 lint happy.
63  *
64  * Revision 2.0  1994/09/28  10:36:06  neelin
65  * Release of minc version 0.2
66  *
67  * Revision 1.9  94/09/28  10:35:39  neelin
68  * Pre-release
69  *
70  * Revision 1.8  94/09/23  16:42:35  neelin
71  * Changed acr_nema_io to acr_io and acr_nema_test to acr_test.
72  *
73  * Revision 1.7  94/05/18  08:47:43  neelin
74  * Changed some ACR_OTHER_ERROR's to ACR_ABNORMAL_END_OF_OUTPUT.
75  *
76  * Revision 1.6  94/04/07  10:03:40  neelin
77  * Added status ACR_ABNORMAL_END_OF_INPUT and changed some ACR_PROTOCOL_ERRORs
78  * to that or ACR_OTHER_ERROR.
79  * Added #ifdef lint to DEFINE_ELEMENT.
80  *
81  * Revision 1.5  94/01/06  13:30:57  neelin
82  * Changed acr_need_invert to a public function.
83  *
84  * Revision 1.4  93/11/30  12:18:34  neelin
85  * Handle MALLOC returning NULL because of extremely large data element length.
86  *
87  * Revision 1.3  93/11/25  10:34:34  neelin
88  * Added routine to test byte-ordering of input.
89  *
90  * Revision 1.2  93/11/24  11:24:48  neelin
91  * Changed short to unsigned short.
92  *
93  * Revision 1.1  93/11/19  12:47:35  neelin
94  * Initial revision
95  *
96 @COPYRIGHT  :
97               Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre,
98               Montreal Neurological Institute, McGill University.
99               Permission to use, copy, modify, and distribute this
100               software and its documentation for any purpose and without
101               fee is hereby granted, provided that the above copyright
102               notice appear in all copies.  The author and McGill University
103               make no representations about the suitability of this
104               software for any purpose.  It is provided "as is" without
105               express or implied warranty.
106 ---------------------------------------------------------------------------- */
107 
108 #include <stdlib.h>
109 #include <stdio.h>
110 #include <limits.h>
111 #include <acr_nema.h>
112 
113 /* Define constants */
114 #if (!defined(TRUE) || !defined(FALSE))
115 #  define TRUE 1
116 #  define FALSE 0
117 #endif
118 
119 #define ACR_BYTE_ORDER_DEFAULT ACR_LITTLE_ENDIAN
120 
121 #define ACR_VR_ENCODING_DEFAULT ACR_IMPLICIT_VR
122 
123 /* Define types */
124 typedef struct {
125    Acr_byte_order byte_order;
126    Acr_VR_encoding_type vr_encoding;
127    int ignore_nonfatal_protocol_errors;
128 } *Data_Info;
129 
130 /* Private functions */
131 static int test_vr(char vr_to_test[2], char *vr_list[]);
132 static int is_sequence_vr(char vr_to_test[2]);
133 static int is_special_vr(char vr_to_test[2]);
134 static Data_Info get_data_info(Acr_File *afp);
135 static void invert_values(Acr_byte_order byte_order,
136                           long nvals, size_t value_size,
137                           void *input_value, void *mach_value);
138 
139 /* Macros */
140 #define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(a[0]))
141 
142 /* ----------------------------- MNI Header -----------------------------------
143 @NAME       : is_sequence_vr
144               is_special_vr
145 @INPUT      : vr_to_test - Two character array containing value representation
146 @OUTPUT     : (none)
147 @RETURNS    : TRUE if vr is in appropriate list
148 @DESCRIPTION: These routines test VR against various lists. is_sequence_vr
149               checks for a sequence and is_special_vr checks for a VR
150               with different fields
151 @METHOD     :
152 @GLOBALS    :
153 @CALLS      :
154 @CREATED    : January 29, 1997 (Peter Neelin)
155 @MODIFIED   :
156 ---------------------------------------------------------------------------- */
test_vr(char vr_to_test[2],char * vr_list[])157 static int test_vr(char vr_to_test[2], char *vr_list[])
158 {
159    int found_special, i;
160 
161    found_special = FALSE;
162    for (i=0; vr_list[i] != NULL; i++) {
163       if ((vr_to_test[0] == vr_list[i][0]) &&
164           (vr_to_test[1] == vr_list[i][1])) {
165          found_special = TRUE;
166          break;
167       }
168    }
169 
170    return found_special;
171 }
172 
is_sequence_vr(char vr_to_test[2])173 static int is_sequence_vr(char vr_to_test[2])
174 {
175    static char *sequence_vrs[] = {"SQ", NULL};
176    return test_vr(vr_to_test, sequence_vrs);
177 }
178 
is_special_vr(char vr_to_test[2])179 static int is_special_vr(char vr_to_test[2])
180 {
181    static char *special_vrs[] = {"OB", "OW", "SQ", "UN", NULL};
182    return test_vr(vr_to_test, special_vrs);
183 }
184 
185 /* ----------------------------- MNI Header -----------------------------------
186 @NAME       : get_data_info
187 @INPUT      : afp - i/o stream
188 @OUTPUT     : (none)
189 @RETURNS    : Pointer to data info
190 @DESCRIPTION: Checks that the i/o stream has the appropriate structure as
191               client data and returns a pointer to the structure.
192 @METHOD     :
193 @GLOBALS    :
194 @CALLS      :
195 @CREATED    : February 14, 1997 (Peter Neelin)
196 @MODIFIED   :
197 ---------------------------------------------------------------------------- */
get_data_info(Acr_File * afp)198 static Data_Info get_data_info(Acr_File *afp)
199 {
200    Data_Info data_info;
201 
202    data_info = acr_file_get_client_data(afp);
203    if (data_info == NULL) {
204       data_info = MALLOC(sizeof(*data_info));
205       data_info->byte_order = ACR_BYTE_ORDER_DEFAULT;
206       data_info->vr_encoding = ACR_VR_ENCODING_DEFAULT;
207       data_info->ignore_nonfatal_protocol_errors = FALSE;
208       acr_file_set_client_data(afp, (void *) data_info);
209    }
210    return data_info;
211 }
212 
213 /* ----------------------------- MNI Header -----------------------------------
214 @NAME       : acr_set_byte_order
215 @INPUT      : afp - i/o stream
216               byte_order - ACR_LITTLE_ENDIAN or ACR_BIG_ENDIAN.
217 @OUTPUT     : (none)
218 @RETURNS    : (nothing)
219 @DESCRIPTION: Allows a user to set the byte ordering for an i/o stream.
220 @METHOD     :
221 @GLOBALS    :
222 @CALLS      :
223 @CREATED    : January 29, 1997 (Peter Neelin)
224 @MODIFIED   :
225 ---------------------------------------------------------------------------- */
acr_set_byte_order(Acr_File * afp,Acr_byte_order byte_order)226 void acr_set_byte_order(Acr_File *afp, Acr_byte_order byte_order)
227 {
228    Data_Info data_info;
229 
230    /* Get data info pointer */
231    data_info = get_data_info(afp);
232 
233    /* Set the byte ordering */
234    data_info->byte_order = byte_order;
235 
236 }
237 
238 /* ----------------------------- MNI Header -----------------------------------
239 @NAME       : acr_get_byte_order
240 @INPUT      : afp - i/o stream
241 @OUTPUT     : (none)
242 @RETURNS    : Byte ordering of stream
243 @DESCRIPTION: Allows one to get the byte ordering for an i/o stream.
244 @METHOD     :
245 @GLOBALS    :
246 @CALLS      :
247 @CREATED    : January 29, 1997 (Peter Neelin)
248 @MODIFIED   :
249 ---------------------------------------------------------------------------- */
acr_get_byte_order(Acr_File * afp)250 Acr_byte_order acr_get_byte_order(Acr_File *afp)
251 {
252    Data_Info data_info;
253 
254    /* Get data info pointer */
255    data_info = get_data_info(afp);
256 
257    /* Return the byte ordering */
258    return data_info->byte_order;
259 }
260 
261 /* ----------------------------- MNI Header -----------------------------------
262 @NAME       : acr_get_machine_byte_order
263 @INPUT      : (none)
264 @OUTPUT     : (none)
265 @RETURNS    : Byte ordering for this machine.
266 @DESCRIPTION: Gets the byte ordering for the machine on which the program is
267               running.
268 @METHOD     :
269 @GLOBALS    :
270 @CALLS      :
271 @CREATED    : February 14, 1997 (Peter Neelin)
272 @MODIFIED   :
273 ---------------------------------------------------------------------------- */
acr_get_machine_byte_order(void)274 int acr_get_machine_byte_order(void)
275 {
276    int dummy = 1;
277    char *ptr = (char *) &dummy;
278 
279    if ((int) ptr[0] == 1)
280       return ACR_LITTLE_ENDIAN;
281    else if ((int) ptr[sizeof(int)-1] == 1)
282       return ACR_BIG_ENDIAN;
283    else {
284       (void) fprintf(stderr,
285          "Internal error: Cannot figure out machine byte order!\n");
286       exit(EXIT_FAILURE);
287       return ACR_BIG_ENDIAN;
288    }
289 
290 }
291 
292 /* ----------------------------- MNI Header -----------------------------------
293 @NAME       : acr_need_invert
294 @INPUT      : byte_order - byte_order of foreign data
295 @OUTPUT     : (none)
296 @RETURNS    : TRUE if need to invert shorts and longs
297 @DESCRIPTION: Indicates whether we need to swap bytes for shorts and longs
298               to convert between the given byte ordering and the machine
299               byte ordering.
300 @METHOD     :
301 @GLOBALS    :
302 @CALLS      :
303 @CREATED    : November 10, 1993 (Peter Neelin)
304 @MODIFIED   : January 29, 1997 (P.N.)
305 ---------------------------------------------------------------------------- */
acr_need_invert(Acr_byte_order byte_order)306 int acr_need_invert(Acr_byte_order byte_order)
307 {
308    return (acr_get_machine_byte_order() != byte_order);
309 }
310 
311 /* ----------------------------- MNI Header -----------------------------------
312 @NAME       : acr_set_vr_encoding
313 @INPUT      : afp - i/o stream
314               vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR
315 @OUTPUT     : (none)
316 @RETURNS    : (nothing)
317 @DESCRIPTION: Allows a user to set the vr encoding type for an i/o stream.
318 @METHOD     :
319 @GLOBALS    :
320 @CALLS      :
321 @CREATED    : January 29, 1997 (Peter Neelin)
322 @MODIFIED   :
323 ---------------------------------------------------------------------------- */
acr_set_vr_encoding(Acr_File * afp,Acr_VR_encoding_type vr_encoding)324 void acr_set_vr_encoding(Acr_File *afp, Acr_VR_encoding_type vr_encoding)
325 {
326    Data_Info data_info;
327 
328    /* Get data info pointer */
329    data_info = get_data_info(afp);
330 
331    /* Set the VR encoding */
332    data_info->vr_encoding = vr_encoding;
333 
334 }
335 
336 /* ----------------------------- MNI Header -----------------------------------
337 @NAME       : acr_get_vr_encoding
338 @INPUT      : afp - i/o stream
339 @OUTPUT     : (none)
340 @RETURNS    : VR encoding of stream
341 @DESCRIPTION: Allows one to get the vr encoding for an i/o stream
342 @METHOD     :
343 @GLOBALS    :
344 @CALLS      :
345 @CREATED    : January 29, 1997 (Peter Neelin)
346 @MODIFIED   :
347 ---------------------------------------------------------------------------- */
acr_get_vr_encoding(Acr_File * afp)348 Acr_VR_encoding_type acr_get_vr_encoding(Acr_File *afp)
349 {
350    Data_Info data_info;
351 
352    /* Get data info pointer */
353    data_info = get_data_info(afp);
354 
355    /* Return the VR encoding */
356    return data_info->vr_encoding;
357 
358 }
359 
360 /* ----------------------------- MNI Header -----------------------------------
361 @NAME       : acr_set_ignore_errors
362 @INPUT      : afp - i/o stream
363               ignore_nonfatal_protocol_errors - if TRUE then non-fatal
364                  protocol errors will be ignored
365 @OUTPUT     : (none)
366 @RETURNS    : (nothing)
367 @DESCRIPTION: Allows a user to indicate whether to ignore protocol errors
368               that can be ignored.
369 @METHOD     :
370 @GLOBALS    :
371 @CALLS      :
372 @CREATED    : April 28, 2000 (Peter Neelin)
373 @MODIFIED   :
374 ---------------------------------------------------------------------------- */
acr_set_ignore_errors(Acr_File * afp,int ignore_nonfatal_protocol_errors)375 void acr_set_ignore_errors(Acr_File *afp, int ignore_nonfatal_protocol_errors)
376 {
377    Data_Info data_info;
378 
379    /* Get data info pointer */
380    data_info = get_data_info(afp);
381 
382    /* Set the flag */
383    data_info->ignore_nonfatal_protocol_errors =
384       ignore_nonfatal_protocol_errors;
385 
386 }
387 
388 /* ----------------------------- MNI Header -----------------------------------
389 @NAME       : acr_ignore_protocol_errors
390 @INPUT      : afp - i/o stream
391 @OUTPUT     : (none)
392 @RETURNS    : TRUE if stream is set to ignore nonfatal protocol errors
393 @DESCRIPTION: Allows one to get the ignore errors flag for a stream
394 @METHOD     :
395 @GLOBALS    :
396 @CALLS      :
397 @CREATED    : April 28, 2000 (Peter Neelin)
398 @MODIFIED   :
399 ---------------------------------------------------------------------------- */
acr_ignore_protocol_errors(Acr_File * afp)400 int acr_ignore_protocol_errors(Acr_File *afp)
401 {
402    Data_Info data_info;
403 
404    /* Get data info pointer */
405    data_info = get_data_info(afp);
406 
407    /* Return the VR encoding */
408    return data_info->ignore_nonfatal_protocol_errors;
409 
410 }
411 
412 /* ----------------------------- MNI Header -----------------------------------
413 @NAME       : acr_reverse_byte_order
414 @INPUT      : nvals - number of values to invert
415               value_size - length of each value
416               input_values - pointer to array of input values
417 @OUTPUT     : output_values - pointer to array of inverted values or NULL
418 @RETURNS    : (nothing)
419 @DESCRIPTION: Reverses byte-ordering of an array of values. Will reverse
420               an array in place if input_values and output_values point to
421               the same array or if output_values is NULL.
422 @METHOD     :
423 @GLOBALS    :
424 @CALLS      :
425 @CREATED    : February 14, 1997 (Peter Neelin)
426 @MODIFIED   :
427 ---------------------------------------------------------------------------- */
acr_reverse_byte_order(long nvals,size_t value_size,void * input_values,void * output_values)428 void acr_reverse_byte_order(long nvals, size_t value_size,
429                             void *input_values, void *output_values)
430 {
431    long i, jlow, jhigh;
432    char *ptr1, *ptr2, v0, v1;
433    int nbytes;
434 
435    /* Get data pointers and check whether output_values is NULL */
436    ptr1 = (char *) input_values;
437    ptr2 = (char *) output_values;
438    if (ptr2 == NULL) ptr2 = ptr1;
439 
440    /* Copy values from both ends at the same time and stop in the middle */
441    nbytes = (value_size+1)/2;
442    for (i=0; i<nvals; i++) {
443       for (jlow=0; jlow<nbytes; jlow++) {
444          jhigh = value_size - jlow - 1;
445          v0 = ptr1[jhigh];
446          v1 = ptr1[jlow];
447          ptr2[jlow] = v0;
448          ptr2[jhigh] = v1;
449       }
450       ptr1 += value_size;
451       ptr2 += value_size;
452    }
453 
454 }
455 
456 /* ----------------------------- MNI Header -----------------------------------
457 @NAME       : invert_values
458 @INPUT      : byte_order - byte ordering for input values
459               nvals - number of values to invert
460               value_size - length of each value
461               input_value - pointer to array of input values
462 @OUTPUT     : mach_value - pointer to array of inverted values
463 @RETURNS    : (nothing)
464 @DESCRIPTION: Reverses byte-ordering of an array of values to match machine
465               byte order if necessary, otherwise the values are just copied.
466 @METHOD     :
467 @GLOBALS    :
468 @CALLS      :
469 @CREATED    : January 31, 1997 (Peter Neelin)
470 @MODIFIED   :
471 ---------------------------------------------------------------------------- */
invert_values(Acr_byte_order byte_order,long nvals,size_t value_size,void * input_value,void * mach_value)472 static void invert_values(Acr_byte_order byte_order,
473                           long nvals, size_t value_size,
474                           void *input_value, void *mach_value)
475 {
476    long i;
477    char *ptr1, *ptr2;
478 
479    /* Check whether a flip is needed */
480    if (acr_need_invert(byte_order)) {
481       acr_reverse_byte_order(nvals, value_size, input_value, mach_value);
482    }
483    else {
484       ptr1 = (char *) input_value;
485       ptr2 = (char *) mach_value;
486       for (i=0; i<nvals*value_size; i++) {
487          ptr2[i] = ptr1[i];
488       }
489    }
490 
491 }
492 
493 /* ----------------------------- MNI Header -----------------------------------
494 @NAME       : acr_get_short
495 @INPUT      : byte_order - byte ordering for input values
496               nvals - number of values to convert to short
497               input_value - pointer to array of input values
498 @OUTPUT     : mach_value - pointer to array of shorts
499 @RETURNS    : (nothing)
500 @DESCRIPTION: Converts input values to shorts.
501 @METHOD     :
502 @GLOBALS    :
503 @CALLS      :
504 @CREATED    : November 10, 1993 (Peter Neelin)
505 @MODIFIED   :
506 ---------------------------------------------------------------------------- */
acr_get_short(Acr_byte_order byte_order,long nvals,void * input_value,Acr_Short * mach_value)507 void acr_get_short(Acr_byte_order byte_order,
508                    long nvals, void *input_value,
509                    Acr_Short *mach_value)
510 {
511    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_SHORT,
512                  input_value, mach_value);
513    return;
514 }
515 
516 /* ----------------------------- MNI Header -----------------------------------
517 @NAME       : acr_get_long
518 @INPUT      : byte_order - byte ordering for input values
519               nvals - number of values to convert to long
520               input_value - pointer to array of input values
521 @OUTPUT     : mach_value - pointer to array of longs
522 @RETURNS    : (nothing)
523 @DESCRIPTION: Converts input values to longs.
524 @METHOD     :
525 @GLOBALS    :
526 @CALLS      :
527 @CREATED    : November 10, 1993 (Peter Neelin)
528 @MODIFIED   :
529 ---------------------------------------------------------------------------- */
acr_get_long(Acr_byte_order byte_order,long nvals,void * input_value,Acr_Long * mach_value)530 void acr_get_long(Acr_byte_order byte_order,
531                   long nvals, void *input_value, Acr_Long *mach_value)
532 {
533    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_LONG,
534                  input_value, mach_value);
535    return;
536 }
537 
538 /* ----------------------------- MNI Header -----------------------------------
539 @NAME       : acr_get_float
540 @INPUT      : byte_order - byte ordering for input values
541               nvals - number of values to convert to float
542               input_value - pointer to array of input values
543 @OUTPUT     : mach_value - pointer to array of floats
544 @RETURNS    : (nothing)
545 @DESCRIPTION: Converts input values to floats. This will only work properly
546               on machines that support IEEE floating-point representation.
547 @METHOD     :
548 @GLOBALS    :
549 @CALLS      :
550 @CREATED    : February 4, 1997 (Peter Neelin)
551 @MODIFIED   :
552 ---------------------------------------------------------------------------- */
acr_get_float(Acr_byte_order byte_order,long nvals,void * input_value,Acr_Float * mach_value)553 void acr_get_float(Acr_byte_order byte_order,
554                    long nvals, void *input_value, Acr_Float *mach_value)
555 {
556    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_FLOAT,
557                  input_value, mach_value);
558    return;
559 }
560 
561 /* ----------------------------- MNI Header -----------------------------------
562 @NAME       : acr_get_double
563 @INPUT      : byte_order - byte ordering for input values
564               nvals - number of values to convert to double
565               input_value - pointer to array of input values
566 @OUTPUT     : mach_value - pointer to array of doubles
567 @RETURNS    : (nothing)
568 @DESCRIPTION: Converts input values to doubles. This will only work properly
569               on machines that support IEEE floating-point representation.
570 @METHOD     :
571 @GLOBALS    :
572 @CALLS      :
573 @CREATED    : February 4, 1997 (Peter Neelin)
574 @MODIFIED   :
575 ---------------------------------------------------------------------------- */
acr_get_double(Acr_byte_order byte_order,long nvals,void * input_value,Acr_Double * mach_value)576 void acr_get_double(Acr_byte_order byte_order,
577                     long nvals, void *input_value, Acr_Double *mach_value)
578 {
579    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_DOUBLE,
580                  input_value, mach_value);
581    return;
582 }
583 
584 /* ----------------------------- MNI Header -----------------------------------
585 @NAME       : acr_put_short
586 @INPUT      : byte_order - byte ordering for output values
587               nvals - number of values to convert from short
588               mach_value - pointer to array of shorts
589 @OUTPUT     : output_value - pointer to array of output values
590 @RETURNS    : (nothing)
591 @DESCRIPTION: Converts shorts to output values.
592 @METHOD     :
593 @GLOBALS    :
594 @CALLS      :
595 @CREATED    : November 10, 1993 (Peter Neelin)
596 @MODIFIED   :
597 ---------------------------------------------------------------------------- */
acr_put_short(Acr_byte_order byte_order,long nvals,Acr_Short * mach_value,void * output_value)598 void acr_put_short(Acr_byte_order byte_order,
599                    long nvals, Acr_Short *mach_value,
600                    void *output_value)
601 {
602    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_SHORT,
603                  mach_value, output_value);
604    return;
605 }
606 
607 /* ----------------------------- MNI Header -----------------------------------
608 @NAME       : acr_put_long
609 @INPUT      : byte_order - byte ordering for output values
610               nvals - number of values to convert from long
611               mach_value - pointer to array of longs
612 @OUTPUT     : output_value - pointer to array of output values
613 @RETURNS    : (nothing)
614 @DESCRIPTION: Converts longs to output values.
615 @METHOD     :
616 @GLOBALS    :
617 @CALLS      :
618 @CREATED    : November 10, 1993 (Peter Neelin)
619 @MODIFIED   :
620 ---------------------------------------------------------------------------- */
acr_put_long(Acr_byte_order byte_order,long nvals,Acr_Long * mach_value,void * output_value)621 void acr_put_long(Acr_byte_order byte_order,
622                   long nvals, Acr_Long *mach_value, void *output_value)
623 {
624    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_LONG,
625                  mach_value, output_value);
626    return;
627 }
628 
629 /* ----------------------------- MNI Header -----------------------------------
630 @NAME       : acr_put_float
631 @INPUT      : byte_order - byte ordering for output values
632               nvals - number of values to convert from float
633               mach_value - pointer to array of longs
634 @OUTPUT     : output_value - pointer to array of output values
635 @RETURNS    : (nothing)
636 @DESCRIPTION: Converts floats to output values.
637 @METHOD     :
638 @GLOBALS    :
639 @CALLS      :
640 @CREATED    : November 10, 1993 (Peter Neelin)
641 @MODIFIED   :
642 ---------------------------------------------------------------------------- */
acr_put_float(Acr_byte_order byte_order,long nvals,Acr_Float * mach_value,void * output_value)643 void acr_put_float(Acr_byte_order byte_order,
644                    long nvals, Acr_Float *mach_value, void *output_value)
645 {
646    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_FLOAT,
647                  mach_value, output_value);
648    return;
649 }
650 
651 /* ----------------------------- MNI Header -----------------------------------
652 @NAME       : acr_put_double
653 @INPUT      : byte_order - byte ordering for output values
654               nvals - number of values to convert from double
655               mach_value - pointer to array of longs
656 @OUTPUT     : output_value - pointer to array of output values
657 @RETURNS    : (nothing)
658 @DESCRIPTION: Converts doubles to output values.
659 @METHOD     :
660 @GLOBALS    :
661 @CALLS      :
662 @CREATED    : November 10, 1993 (Peter Neelin)
663 @MODIFIED   :
664 ---------------------------------------------------------------------------- */
acr_put_double(Acr_byte_order byte_order,long nvals,Acr_Double * mach_value,void * output_value)665 void acr_put_double(Acr_byte_order byte_order,
666                     long nvals, Acr_Double *mach_value, void *output_value)
667 {
668    invert_values(byte_order, nvals, (size_t) ACR_SIZEOF_DOUBLE,
669                  mach_value, output_value);
670    return;
671 }
672 
673 /* ----------------------------- MNI Header -----------------------------------
674 @NAME       : acr_skip_input_data
675 @INPUT      : afp
676               nbytes_to_skip
677 @OUTPUT     : (none)
678 @RETURNS    : Input status. If an error occurs on the first byte, then
679               ACR_END_OF_INPUT is returned, if an error occurs elsewhere,
680               then ACR_ABNORMAL_END_OF_INPUT is returned, otherwise ACR_OK
681               is returned.
682 @DESCRIPTION: Skips over input data.
683 @METHOD     :
684 @GLOBALS    :
685 @CALLS      :
686 @CREATED    : February 12, 1997 (Peter Neelin)
687 @MODIFIED   :
688 ---------------------------------------------------------------------------- */
acr_skip_input_data(Acr_File * afp,long nbytes_to_skip)689 Acr_Status acr_skip_input_data(Acr_File *afp, long nbytes_to_skip)
690 {
691    long i;
692    int ch;
693 
694    for (i=0; i < nbytes_to_skip; i++) {
695       ch = acr_getc(afp);
696       if (ch == EOF) {
697          break;
698       }
699    }
700 
701    /* Return the status */
702    if (i >= nbytes_to_skip) {
703       return ACR_OK;
704    }
705    else if (acr_get_io_watchpoint(afp) <= 0) {
706       return ACR_REACHED_WATCHPOINT;
707    }
708    else if (i == 0) {
709       return ACR_END_OF_INPUT;
710    }
711    else {
712       return ACR_ABNORMAL_END_OF_INPUT;
713    }
714 }
715 
716 /* ----------------------------- MNI Header -----------------------------------
717 @NAME       : acr_read_buffer
718 @INPUT      : afp
719               nbytes_to_read
720 @OUTPUT     : buffer
721               nbytes_read - if NULL, then this value is ignored, otherwise
722                  the number of bytes actually read in is returned.
723 @RETURNS    : Input status. If an error occurs on the first byte, then
724               ACR_END_OF_INPUT is returned, if an error occurs elsewhere,
725               then ACR_ABNORMAL_END_OF_INPUT is returned, otherwise ACR_OK
726               is returned.
727 @DESCRIPTION: Reads in a buffer of data and optionally returns the number
728               of bytes read
729 @METHOD     :
730 @GLOBALS    :
731 @CALLS      :
732 @CREATED    : February 12, 1997 (Peter Neelin)
733 @MODIFIED   :
734 ---------------------------------------------------------------------------- */
acr_read_buffer(Acr_File * afp,unsigned char buffer[],long nbytes_to_read,long * nbytes_read)735 Acr_Status acr_read_buffer(Acr_File *afp, unsigned char buffer[],
736                            long nbytes_to_read, long *nbytes_read)
737 {
738    long i;
739    int ch;
740 
741    for (i=0; i < nbytes_to_read; i++) {
742       ch = acr_getc(afp);
743       if (ch == EOF) {
744          break;
745       }
746       buffer[i] = (unsigned char) ch;
747    }
748 
749    /* Save the number of bytes read */
750    if (nbytes_read != NULL) {
751       *nbytes_read = i;
752    }
753 
754    /* Return the status */
755    if (i >= nbytes_to_read) {
756       return ACR_OK;
757    }
758    else if (acr_get_io_watchpoint(afp) <= 0) {
759       return ACR_REACHED_WATCHPOINT;
760    }
761    else if (i == 0) {
762       return ACR_END_OF_INPUT;
763    }
764    else {
765       return ACR_ABNORMAL_END_OF_INPUT;
766    }
767 }
768 
769 /* ----------------------------- MNI Header -----------------------------------
770 @NAME       : acr_unget_buffer
771 @INPUT      : afp
772               nbytes_to_unget
773               buffer
774 @OUTPUT     : (none)
775 @RETURNS    : Unget status.
776 @DESCRIPTION: Puts a buffer of data back into the input stream.
777 @METHOD     :
778 @GLOBALS    :
779 @CALLS      :
780 @CREATED    : February 12, 1997 (Peter Neelin)
781 @MODIFIED   :
782 ---------------------------------------------------------------------------- */
acr_unget_buffer(Acr_File * afp,unsigned char buffer[],long nbytes_to_unget)783 Acr_Status acr_unget_buffer(Acr_File *afp, unsigned char buffer[],
784                             long nbytes_to_unget)
785 {
786    long i;
787 
788    for (i=nbytes_to_unget-1; i >= 0; i--) {
789       if (acr_ungetc((int) buffer[i], afp) == EOF) {
790          break;
791       }
792    }
793 
794    /* Return the status */
795    if (i >= 0) {
796       return ACR_IO_ERROR;
797    }
798    else {
799       return ACR_OK;
800    }
801 }
802 
803 /* ----------------------------- MNI Header -----------------------------------
804 @NAME       : acr_write_buffer
805 @INPUT      : afp
806               nbytes_to_write
807               buffer
808 @OUTPUT     : nbytes_written
809 @RETURNS    : Output status.
810 @DESCRIPTION: Writes out a buffer of data and optionally returns the number
811               of bytes written
812 @METHOD     :
813 @GLOBALS    :
814 @CALLS      :
815 @CREATED    : February 12, 1997 (Peter Neelin)
816 @MODIFIED   :
817 ---------------------------------------------------------------------------- */
acr_write_buffer(Acr_File * afp,unsigned char buffer[],long nbytes_to_write,long * nbytes_written)818 Acr_Status acr_write_buffer(Acr_File *afp, unsigned char buffer[],
819                             long nbytes_to_write, long *nbytes_written)
820 {
821    long i;
822 
823    for (i=0; i < nbytes_to_write; i++) {
824       if (acr_putc(buffer[i], afp) == EOF) {
825          break;
826       }
827    }
828 
829    /* Save the number of bytes written */
830    if (nbytes_written != NULL) {
831       *nbytes_written = i;
832    }
833 
834    /* Return the status */
835    if (i < nbytes_to_write) {
836       return ACR_ABNORMAL_END_OF_OUTPUT;
837    }
838    else {
839       return ACR_OK;
840    }
841 }
842 
843 /* ----------------------------- MNI Header -----------------------------------
844 @NAME       : acr_test_byte_order
845 @INPUT      : afp
846 @OUTPUT     : (none)
847 @RETURNS    : status.
848 @DESCRIPTION: Tests input for byte ordering to use. The test is done by
849               looking at the length of the first element. First a test is
850               done for implicit VR, assuming that the length of the first
851               element is less than 64K and greater than zero, and we try
852               the two possible byte orders. If the VR encoding is explicit,
853               then we have two shortwords (2-bytes), both of which are
854               non-zero and the longword (4 bytes) will be greater than 64K.
855               In this case, we test the 2-byte length looking for a length
856               that is less than 256 bytes. If that fails, than we revert
857               to the original byte order.
858 @METHOD     :
859 @GLOBALS    :
860 @CALLS      :
861 @CREATED    : November 10, 1993 (Peter Neelin)
862 @MODIFIED   : January 29, 1997 (P.N.)
863 ---------------------------------------------------------------------------- */
acr_test_byte_order(Acr_File * afp)864 Acr_Status acr_test_byte_order(Acr_File *afp)
865 {
866    long buflen;
867    unsigned char buffer[2*ACR_SIZEOF_SHORT+ACR_SIZEOF_LONG];
868    Acr_Long data_length;
869    Acr_Short data_length2;
870    Acr_Status status;
871    Acr_byte_order byte_order, old_byte_order;
872 
873 #define ACR_TEST_MAX USHRT_MAX
874 #define ACR_TEST_MAX2 UCHAR_MAX
875 
876    /* Save old byte ordering */
877    old_byte_order = acr_get_byte_order(afp);
878 
879    /* Read in group id, element id and length of data */
880    status = acr_read_buffer(afp, buffer, SIZEOF_ARRAY(buffer), &buflen);
881    if (status != ACR_OK) return status;
882 
883    /* Put the characters back */
884    status = acr_unget_buffer(afp, buffer, buflen);
885    if (status != ACR_OK) return status;
886 
887    /* Test data length (the first element should be a group length).
888       Try big-endian ordering first. */
889    byte_order = ACR_BIG_ENDIAN;
890    acr_set_byte_order(afp, byte_order);
891    acr_get_long(byte_order, 1, &buffer[2*ACR_SIZEOF_SHORT],
892                 &data_length);
893 
894    /* If that doesn't work, set it to little-endian ordering. */
895    if (data_length >= ACR_TEST_MAX) {
896       byte_order = ACR_LITTLE_ENDIAN;
897       acr_set_byte_order(afp, byte_order);
898       acr_get_long(byte_order, 1, &buffer[2*ACR_SIZEOF_SHORT],
899                    &data_length);
900    }
901 
902    /* If one of them worked, then it means that we have implicit VR
903       encoding since we didn't look for a VR field */
904    if (data_length < ACR_TEST_MAX) {
905       acr_set_vr_encoding(afp, ACR_IMPLICIT_VR);
906    }
907 
908    /* Otherwise we probably have explicit vr encoding. */
909    else {
910       acr_set_vr_encoding(afp, ACR_EXPLICIT_VR);
911 
912       /* Check the length in this case to see if it small. The default
913          will be little endian. */
914       byte_order = ACR_BIG_ENDIAN;
915       acr_set_byte_order(afp, byte_order);
916       acr_get_short(byte_order, 1, &buffer[3*ACR_SIZEOF_SHORT],
917                    &data_length2);
918       if (data_length2 >= ACR_TEST_MAX2) {
919          byte_order = ACR_LITTLE_ENDIAN;
920          acr_set_byte_order(afp, byte_order);
921          acr_get_short(byte_order, 1, &buffer[3*ACR_SIZEOF_SHORT],
922                        &data_length2);
923       }
924       if (data_length2 >= ACR_TEST_MAX2) {
925          /* If we get here, we have completely failed to make sense of
926           * the byte ordering.
927           */
928          acr_set_byte_order(afp, old_byte_order);
929       }
930    }
931 
932    return ACR_OK;
933 
934 }
935 
936 /* ----------------------------- MNI Header -----------------------------------
937 @NAME       : acr_copy_file_encoding
938 @INPUT      : afp1 - source stream
939 @OUTPUT     : afp2 - target stream
940 @RETURNS    : (nothing)
941 @DESCRIPTION: Copies the byte ordering and VR encoding from one i/o stream
942               to another.
943 @METHOD     :
944 @GLOBALS    :
945 @CALLS      :
946 @CREATED    : February 14, 1997 (Peter Neelin)
947 @MODIFIED   :
948 ---------------------------------------------------------------------------- */
acr_copy_file_encoding(Acr_File * afp1,Acr_File * afp2)949 void acr_copy_file_encoding(Acr_File *afp1, Acr_File *afp2)
950 {
951    acr_set_byte_order(afp2, acr_get_byte_order(afp1));
952    acr_set_vr_encoding(afp2, acr_get_vr_encoding(afp1));
953 }
954 
955 /* ----------------------------- MNI Header -----------------------------------
956 @NAME       : acr_get_element_header_size
957 @INPUT      : vr_name - 2-letter name of Vr
958               vr_encoding - ACR_EXPLICIT_VR or ACR_IMPLICIT_VR
959 @OUTPUT     : (none)
960 @RETURNS    : length of header
961 @DESCRIPTION: Calculates the length of the element header (excluding data)
962 @METHOD     :
963 @GLOBALS    :
964 @CALLS      :
965 @CREATED    : February 4, 1997 (Peter Neelin)
966 @MODIFIED   :
967 ---------------------------------------------------------------------------- */
acr_get_element_header_size(char vr_name[2],Acr_VR_encoding_type vr_encoding)968 int acr_get_element_header_size(char vr_name[2],
969                                 Acr_VR_encoding_type vr_encoding)
970 {
971    int length;
972 
973    length = 2*ACR_SIZEOF_SHORT + ACR_SIZEOF_LONG;
974    if ((vr_encoding == ACR_EXPLICIT_VR) && is_special_vr(vr_name)) {
975       length += ACR_SIZEOF_LONG;
976    }
977    return length;
978 }
979 
980 /* ----------------------------- MNI Header -----------------------------------
981 @NAME       : acr_peek_at_next_element_id
982 @INPUT      : afp - Acr_File pointer from which to read
983 @OUTPUT     : group_id
984               element_id
985 @RETURNS    : Status
986 @DESCRIPTION: Peeks ahead to get the group and element ids of the next
987               element. The file position is restored. If a read error occurs,
988               then group_id and element_id are set to INT_MIN and the status
989               is returned.
990 @METHOD     :
991 @GLOBALS    :
992 @CALLS      :
993 @CREATED    : February 5, 1997 (Peter Neelin)
994 @MODIFIED   :
995 ---------------------------------------------------------------------------- */
acr_peek_at_next_element_id(Acr_File * afp,int * group_id,int * element_id)996 Acr_Status acr_peek_at_next_element_id(Acr_File *afp,
997                                        int *group_id, int *element_id)
998 {
999    long buflen;
1000    unsigned char buffer[2*ACR_SIZEOF_SHORT];
1001    Acr_Short svalue;
1002    Acr_Status status, status2;
1003    Acr_byte_order byte_order;
1004 
1005    /* Set default values */
1006    status = ACR_OK;
1007    *group_id = INT_MIN;
1008    *element_id = INT_MIN;
1009 
1010    /* Read in the values */
1011    status = acr_read_buffer(afp, buffer, SIZEOF_ARRAY(buffer), &buflen);
1012 
1013    /* Put them back */
1014    status2 = acr_unget_buffer(afp, buffer, buflen);
1015    if (status == ACR_OK) status = status2;
1016 
1017    /* Check for input error */
1018    if (status != ACR_OK) return status;
1019 
1020    /* Get the id's */
1021    byte_order = acr_get_byte_order(afp);
1022    acr_get_short(byte_order, 1, &buffer[0], &svalue);
1023    *group_id = (int)svalue;
1024    acr_get_short(byte_order, 1, &buffer[ACR_SIZEOF_SHORT], &svalue);
1025    *element_id = (int)svalue;
1026 
1027    return status;
1028 
1029 }
1030 
1031 /* ----------------------------- MNI Header -----------------------------------
1032 @NAME       : acr_read_one_element
1033 @INPUT      : afp - Acr_File pointer from which to read
1034 @OUTPUT     : group_id - ACR-NEMA group id
1035               element_id - ACR-NEMA element id
1036               vr_name - 2 character string giving value representation.
1037                  Two NULs are returned if VR is unknown.
1038               data_length - length of data to follow. Value
1039                  ACR_VARIABLE_LENGTH is returned for undefined length elements
1040                  in which case the data portion is not read in.
1041               data_pointer - pointer to data. Space is allocated by this
1042                  routine. One additional byte is allocated and set to
1043                  zero so that the data can be treated as a string. If a
1044                  sequence is encountered, then NULL is returned.
1045 @RETURNS    : Status.
1046 @DESCRIPTION: Routine to read in one ACR-NEMA element.
1047 @METHOD     :
1048 @GLOBALS    :
1049 @CALLS      :
1050 @CREATED    : November 10, 1993 (Peter Neelin)
1051 @MODIFIED   : January 29, 1997 (P.N.)
1052 ---------------------------------------------------------------------------- */
acr_read_one_element(Acr_File * afp,int * group_id,int * element_id,char vr_name[],long * data_length,char ** data_pointer)1053 Acr_Status acr_read_one_element(Acr_File *afp,
1054                                 int *group_id, int *element_id,
1055                                 char vr_name[],
1056                                 long *data_length, char **data_pointer)
1057 {
1058    long buflen;
1059    unsigned char buffer[2*ACR_SIZEOF_SHORT+ACR_SIZEOF_LONG];
1060    Acr_Short grpid, elid, sval;
1061    Acr_Long datalen;
1062    size_t size_allocated;
1063    int offset;
1064    Acr_byte_order byte_order;
1065    Acr_Status status;
1066 
1067    /* Get byte ordering */
1068    byte_order = acr_get_byte_order(afp);
1069 
1070    /* Read in group id, element id and length of data */
1071    status = acr_read_buffer(afp, buffer, SIZEOF_ARRAY(buffer), &buflen);
1072    if (status != ACR_OK) return status;
1073    offset = 0;
1074    acr_get_short(byte_order, 1, &buffer[offset], &grpid);
1075    offset += ACR_SIZEOF_SHORT;
1076    *group_id = (int)grpid;
1077    acr_get_short(byte_order, 1, &buffer[offset], &elid);
1078    offset += ACR_SIZEOF_SHORT;
1079    *element_id = (int)elid;
1080 
1081    /* Look for VR and length of data */
1082    if (grpid == ACR_ITEM_GROUP || acr_get_vr_encoding(afp) == ACR_IMPLICIT_VR) {
1083       vr_name[0] = '\0';
1084       vr_name[1] = '\0';
1085       acr_get_long(byte_order, 1, &buffer[offset], &datalen);
1086       offset += ACR_SIZEOF_LONG;
1087    }
1088    else {
1089       vr_name[0] = buffer[offset++];
1090       vr_name[1] = buffer[offset++];
1091       acr_get_short(byte_order, 1, &buffer[offset], &sval);
1092       offset += ACR_SIZEOF_SHORT;
1093       datalen = (Acr_Long)sval;
1094    }
1095 
1096    /* Read in length for special VR's */
1097    if (is_special_vr(vr_name)) {
1098       status = acr_read_buffer(afp, buffer, (long) ACR_SIZEOF_LONG, NULL);
1099       if (status != ACR_OK) return ACR_ABNORMAL_END_OF_INPUT;
1100       acr_get_long(byte_order, 1, &buffer[0], &datalen);
1101    }
1102 
1103    /* Check for undefined length */
1104    if (datalen == ACR_UNDEFINED_ELEMENT_LENGTH) {
1105       *data_length = ACR_VARIABLE_LENGTH;
1106       *data_pointer = NULL;
1107       return ACR_OK;
1108    }
1109    *data_length = (long)datalen;
1110 
1111    /* Check for sequence VR */
1112    if (is_sequence_vr(vr_name)) {
1113       *data_pointer = NULL;
1114       return ACR_OK;
1115    }
1116 
1117    /* Allocate space for the data and null-terminate it */
1118    size_allocated = *data_length + 1;
1119    *data_pointer = MALLOC(size_allocated);
1120    if (*data_pointer == NULL) {
1121       *data_length = 0;
1122       size_allocated = *data_length + 1;
1123       *data_pointer = MALLOC(size_allocated);
1124    }
1125    (*data_pointer)[*data_length] = '\0';
1126 
1127    /* Read in the data */
1128    status = acr_read_buffer(afp, (unsigned char *) *data_pointer,
1129                             *data_length, NULL);
1130    if (status != ACR_OK) {
1131       FREE(*data_pointer);
1132       return ACR_ABNORMAL_END_OF_INPUT;
1133    }
1134 
1135    return ACR_OK;
1136 }
1137 
1138 /* ----------------------------- MNI Header -----------------------------------
1139 @NAME       : acr_write_one_element
1140 @INPUT      : afp - Acr_File pointer from which to read
1141               group_id - ACR-NEMA group id
1142               element_id - ACR-NEMA element id
1143               vr_name - 2 character string giving value representation.
1144                  It is an error to pass in two NULs if explicit VR is used
1145                  (ACR_NO_VR_SPECIFIED is returned).
1146               data_length - length of data to follow. If set to
1147                  ACR_VARIABLE_LENGTH, then the data portion is not written out.
1148               data_pointer - pointer to data. If NULL, then no data is
1149                  written.
1150 @OUTPUT     : (nothing)
1151 @RETURNS    : Status.
1152 @DESCRIPTION: Routine to write out one ACR-NEMA element.
1153 @METHOD     :
1154 @GLOBALS    :
1155 @CALLS      :
1156 @CREATED    : November 10, 1993 (Peter Neelin)
1157 @MODIFIED   : January 29, 1997 (P.N.)
1158 ---------------------------------------------------------------------------- */
acr_write_one_element(Acr_File * afp,int group_id,int element_id,char vr_name[],long data_length,char * data_pointer)1159 Acr_Status acr_write_one_element(Acr_File *afp,
1160                                  int group_id, int element_id,
1161                                  char vr_name[],
1162                                  long data_length, char *data_pointer)
1163 {
1164    long buflen;
1165    unsigned char buffer[2*ACR_SIZEOF_SHORT+2*ACR_SIZEOF_LONG];
1166    Acr_Short grpid, elid, sval;
1167    Acr_Long datalen;
1168    int offset;
1169    Acr_byte_order byte_order;
1170    Acr_Status status;
1171 
1172    buflen = sizeof(buffer)/sizeof(buffer[0]) - ACR_SIZEOF_LONG;
1173 
1174    /* Get byte ordering */
1175    byte_order = acr_get_byte_order(afp);
1176 
1177    /* Get the group id and element id */
1178    offset = 0;
1179    grpid = (Acr_Short) group_id;
1180    acr_put_short(byte_order, 1, &grpid, &buffer[offset]);
1181    offset += ACR_SIZEOF_SHORT;
1182    elid = (Acr_Short) element_id;
1183    acr_put_short(byte_order, 1, &elid, &buffer[offset]);
1184    offset += ACR_SIZEOF_SHORT;
1185 
1186    /* Check data length */
1187    if ((Acr_Long)data_length == ACR_VARIABLE_LENGTH)
1188       datalen = ACR_UNDEFINED_ELEMENT_LENGTH;
1189    else
1190       datalen = (Acr_Long)data_length;
1191 
1192    /* Check whether we need VR */
1193    if (acr_get_vr_encoding(afp) == ACR_IMPLICIT_VR) {
1194       acr_put_long(byte_order, 1, &datalen, &buffer[offset]);
1195       offset += ACR_SIZEOF_LONG;
1196    }
1197    else {
1198       if (vr_name[0] == '\0') return ACR_NO_VR_SPECIFIED;
1199       buffer[offset++] = vr_name[0];
1200       buffer[offset++] = vr_name[1];
1201       if (!is_special_vr(vr_name)) {
1202          sval = (Acr_Short) datalen;
1203          acr_put_short(byte_order, 1, &sval, &buffer[offset]);
1204          offset += ACR_SIZEOF_SHORT;
1205       }
1206       else {
1207          sval = 0;
1208          acr_put_short(byte_order, 1, &sval, &buffer[offset]);
1209          offset += ACR_SIZEOF_SHORT;
1210          acr_put_long(byte_order, 1, &datalen, &buffer[offset]);
1211          offset += ACR_SIZEOF_LONG;
1212          buflen += ACR_SIZEOF_LONG;
1213       }
1214    }
1215 
1216    /* Write it out */
1217    status = acr_write_buffer(afp, buffer, buflen, NULL);
1218    if (status != ACR_OK) return status;
1219 
1220    if ((data_length == ACR_VARIABLE_LENGTH) || (data_pointer == NULL)) {
1221       return ACR_OK;
1222    }
1223 
1224    /* Write out the data */
1225    status = acr_write_buffer(afp, (unsigned char *) data_pointer,
1226                              data_length, NULL);
1227    if (status != ACR_OK) return status;
1228 
1229    return ACR_OK;
1230 }
1231 
1232 /* ----------------------------- MNI Header -----------------------------------
1233 @NAME       : acr_status_string
1234 @INPUT      : status - status code to look up
1235 @OUTPUT     : (nothing)
1236 @RETURNS    : Pointer to string describing status.
1237 @DESCRIPTION: Routine to get a string that describes a status value.
1238 @METHOD     :
1239 @GLOBALS    :
1240 @CALLS      :
1241 @CREATED    : July 10, 1997 (Peter Neelin)
1242 @MODIFIED   :
1243 ---------------------------------------------------------------------------- */
acr_status_string(Acr_Status status)1244 char *acr_status_string(Acr_Status status)
1245 {
1246    char *status_string;
1247 
1248    switch (status) {
1249    case ACR_OK:
1250       status_string = "No error"; break;
1251    case ACR_END_OF_INPUT:
1252       status_string = "End of input"; break;
1253    case ACR_PROTOCOL_ERROR:
1254       status_string = "Protocol error"; break;
1255    case ACR_OTHER_ERROR:
1256       status_string = "Other error"; break;
1257    case ACR_ABNORMAL_END_OF_INPUT:
1258       status_string = "Abnormal end of input"; break;
1259    case ACR_HIGH_LEVEL_ERROR:
1260       status_string = "High-level error"; break;
1261    case ACR_ABNORMAL_END_OF_OUTPUT:
1262       status_string = "Abnormal end of output"; break;
1263    case ACR_REACHED_WATCHPOINT:
1264       status_string = "Reached watchpoint"; break;
1265    case ACR_IO_ERROR:
1266       status_string = "I/O error"; break;
1267    case ACR_NO_VR_SPECIFIED:
1268       status_string = "VR not specified on output"; break;
1269    case ACR_PDU_UID_TOO_LONG:
1270       status_string = "Input PDU UID too long"; break;
1271    case ACR_CONNECTION_TIMEDOUT:
1272       status_string = "Connection timed out"; break;
1273    default:
1274       status_string = "Unknown status"; break;
1275    }
1276 
1277    return status_string;
1278 }
1279 
1280