Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libcam / scsi_cmdparse.c
1 /*
2  * Taken from the original FreeBSD user SCSI library.
3  */
4 /* Copyright (c) 1994 HD Associates
5  * (contact: dufault@hda.com)
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  * This product includes software developed by HD Associates
19  * 4. Neither the name of the HD Associaates nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
35  * $FreeBSD: src/lib/libcam/scsi_cmdparse.c,v 1.3.2.1 2000/08/14 05:42:30 kbyanc Exp $
36  */
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <ctype.h>
40 #include <string.h>
41 #include <sys/errno.h>
42 #include <stdarg.h>
43 #include <fcntl.h>
44
45 #include <cam/cam.h>
46 #include <cam/cam_ccb.h>
47 #include <cam/scsi/scsi_message.h>
48 #include "camlib.h"
49
50 /*
51  * Decode: Decode the data section of a scsireq.  This decodes
52  * trivial grammar:
53  *
54  * fields : field fields
55  *        ;
56  *
57  * field : field_specifier
58  *       | control
59  *       ;
60  *
61  * control : 's' seek_value
62  *       | 's' '+' seek_value
63  *       ;
64  *
65  * seek_value : DECIMAL_NUMBER
66  *       | 'v'                          // For indirect seek, i.e., value from the arg list
67  *       ;
68  *
69  * field_specifier : type_specifier field_width
70  *       | '{' NAME '}' type_specifier field_width
71  *       ;
72  *
73  * field_width : DECIMAL_NUMBER
74  *       ;
75  *
76  * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
77  *       | 'b'                          // Bits
78  *       | 't'                          // Bits
79  *       | 'c'                          // Character arrays
80  *       | 'z'                          // Character arrays with zeroed trailing spaces
81  *       ;
82  *
83  * Notes:
84  * 1. Integral types are swapped into host order.
85  * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
86  * 3. 's' permits "seeking" in the string.  "s+DECIMAL" seeks relative to
87  *    DECIMAL; "sDECIMAL" seeks absolute to decimal.
88  * 4. 's' permits an indirect reference.  "sv" or "s+v" will get the
89  *    next integer value from the arg array.
90  * 5. Field names can be anything between the braces
91  *
92  * BUGS:
93  * i and b types are promoted to ints.
94  *
95  */
96
97 static int
98 do_buff_decode(u_int8_t *databuf, size_t len,
99                void (*arg_put)(void *, int , void *, int, char *),
100                void *puthook, char *fmt, va_list ap)
101 {
102         int assigned = 0;
103         int width;
104         int suppress;
105         int plus;
106         int done = 0;
107         static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
108                                    0x1f, 0x3f, 0x7f, 0xff};
109         int value;
110         u_char *base = databuf;
111         char letter;
112         char field_name[80];
113
114 #       define ARG_PUT(ARG) \
115         do \
116         { \
117                 if (!suppress) \
118                 { \
119                         if (arg_put) \
120                                 (*arg_put)(puthook, (letter == 't' ? \
121                                         'b' : letter), \
122                                         (void *)((long)(ARG)), width, \
123                                         field_name); \
124                         else \
125                                 *(va_arg(ap, int *)) = (ARG); \
126                         assigned++; \
127                 } \
128                 field_name[0] = 0; \
129                 suppress = 0; \
130         } while (0)
131
132         u_char bits = 0;        /* For bit fields */
133         int shift = 0;          /* Bits already shifted out */
134         suppress = 0;
135         field_name[0] = 0;
136
137         while (!done) {
138                 switch(letter = *fmt) {
139                 case ' ':       /* White space */
140                 case '\t':
141                 case '\r':
142                 case '\n':
143                 case '\f':
144                         fmt++;
145                         break;
146
147                 case '#':       /* Comment */
148                         while (*fmt && (*fmt != '\n'))
149                                 fmt++;
150                         if (fmt)
151                                 fmt++;  /* Skip '\n' */
152                         break;
153
154                 case '*':       /* Suppress assignment */
155                         fmt++;
156                         suppress = 1;
157                         break;
158
159                 case '{':       /* Field Name */
160                 {
161                         int i = 0;
162                         fmt++;  /* Skip '{' */
163                         while (*fmt && (*fmt != '}')) {
164                                 if (i < sizeof(field_name))
165                                         field_name[i++] = *fmt;
166
167                                 fmt++;
168                         }
169                         if (fmt)
170                                 fmt++;  /* Skip '}' */
171                         field_name[i] = 0;
172                         break;
173                 }
174
175                 case 't':       /* Bit (field) */
176                 case 'b':       /* Bits */
177                         fmt++;
178                         width = strtol(fmt, &fmt, 10);
179                         if (width > 8)
180                                 done = 1;
181                         else {
182                                 if (shift <= 0) {
183                                         bits = *databuf++;
184                                         shift = 8;
185                                 }
186                                 value = (bits >> (shift - width)) &
187                                          mask[width];
188
189 #if 0
190                                 printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
191                                 shift, bits, value, width, mask[width]);
192 #endif
193
194                                 ARG_PUT(value);
195
196                                 shift -= width;
197                         }
198                         break;
199
200                 case 'i':       /* Integral values */
201                         shift = 0;
202                         fmt++;
203                         width = strtol(fmt, &fmt, 10);
204                         switch(width) {
205                         case 1:
206                                 ARG_PUT(*databuf);
207                                 databuf++;
208                                 break;
209
210                         case 2:
211                                 ARG_PUT((*databuf) << 8 | *(databuf + 1));
212                                 databuf += 2;
213                                 break;
214
215                         case 3:
216                                 ARG_PUT((*databuf) << 16 |
217                                         (*(databuf + 1)) << 8 | *(databuf + 2));
218                                 databuf += 3;
219                                 break;
220
221                         case 4:
222                                 ARG_PUT((*databuf) << 24 |
223                                         (*(databuf + 1)) << 16 |
224                                         (*(databuf + 2)) << 8 |
225                                         *(databuf + 3));
226                                 databuf += 4;
227                                 break;
228
229                         default:
230                                 done = 1;
231                                 break;
232                         }
233
234                         break;
235
236                 case 'c':       /* Characters (i.e., not swapped) */
237                 case 'z':       /* Characters with zeroed trailing
238                                            spaces  */
239                         shift = 0;
240                         fmt++;
241                         width = strtol(fmt, &fmt, 10);
242                         if (!suppress) {
243                                 if (arg_put)
244                                         (*arg_put)(puthook,
245                                                 (letter == 't' ? 'b' : letter),
246                                                 databuf, width, field_name);
247                                 else {
248                                         char *dest;
249                                         dest = va_arg(ap, char *);
250                                         bcopy(databuf, dest, width);
251                                         if (letter == 'z') {
252                                                 char *p;
253                                                 for (p = dest + width - 1;
254                                                      (p >= (char *)dest)
255                                                      && (*p == ' '); p--)
256                                                         *p = 0;
257                                         }
258                                 }
259                                 assigned++;
260                         }
261                         databuf += width;
262                         field_name[0] = 0;
263                         suppress = 0;
264                         break;
265
266                 case 's':       /* Seek */
267                         shift = 0;
268                         fmt++;
269                         if (*fmt == '+') {
270                                 plus = 1;
271                                 fmt++;
272                         } else
273                                 plus = 0;
274
275                         if (tolower(*fmt) == 'v') {
276                                 /*
277                                  * You can't suppress a seek value.  You also
278                                  * can't have a variable seek when you are using
279                                  * "arg_put".
280                                  */
281                                 width = (arg_put) ? 0 : va_arg(ap, int);
282                                 fmt++;
283                         } else
284                                 width = strtol(fmt, &fmt, 10);
285
286                         if (plus)
287                                 databuf += width;       /* Relative seek */
288                         else
289                                 databuf = base + width; /* Absolute seek */
290
291                         break;
292
293                 case 0:
294                         done = 1;
295                         break;
296
297                 default:
298                         fprintf(stderr, "Unknown letter in format: %c\n",
299                                 letter);
300                         fmt++;
301                         break;
302                 }
303         }
304
305         return (assigned);
306 }
307
308 /* next_field: Return the next field in a command specifier.  This
309  * builds up a SCSI command using this trivial grammar:
310  *
311  * fields : field fields
312  *        ;
313  *
314  * field : value
315  *       | value ':' field_width
316  *       ;
317  *
318  * field_width : digit
319  *       | 'i' digit            // i2 = 2 byte integer, i3 = 3 byte integer etc.
320  *       ;
321  *
322  * value : HEX_NUMBER
323  *       | 'v'                          // For indirection.
324  *       ;
325  *
326  * Notes:
327  *  Bit fields are specified MSB first to match the SCSI spec.
328  *
329  * Examples:
330  *  TUR: "0 0 0 0 0 0"
331  *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
332  *
333  * The function returns the value:
334  *  0: For reached end, with error_p set if an error was found
335  *  1: For valid stuff setup
336  *  2: For "v" was entered as the value (implies use varargs)
337  *
338  */
339
340 static int
341 next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name,
342            int n_name, int *error_p, int *suppress_p)
343 {
344         char *p = *pp;
345
346         int something = 0;
347
348         enum {
349                 BETWEEN_FIELDS,
350                 START_FIELD,
351                 GET_FIELD,
352                 DONE,
353         } state;
354
355         int value = 0;
356         int field_size;         /* Default to byte field type... */
357         int field_width;        /* 1 byte wide */
358         int is_error = 0;
359         int suppress = 0;
360
361         field_size = 8;         /* Default to byte field type... */
362         *fmt = 'i';
363         field_width = 1;        /* 1 byte wide */
364         if (name)
365                 *name = 0;
366
367         state = BETWEEN_FIELDS;
368
369         while (state != DONE) {
370                 switch(state) {
371                 case BETWEEN_FIELDS:
372                         if (*p == 0)
373                                 state = DONE;
374                         else if (isspace(*p))
375                                 p++;
376                         else if (*p == '#') {
377                                 while (*p && *p != '\n')
378                                         p++;
379                                 if (p)
380                                         p++;
381                         } else if (*p == '{') {
382                                 int i = 0;
383
384                                 p++;
385
386                                 while (*p && *p != '}') {
387                                         if(name && i < n_name) {
388                                                 name[i] = *p;
389                                                 i++;
390                                         }
391                                         p++;
392                                 }
393
394                                 if(name && i < n_name)
395                                         name[i] = 0;
396
397                                 if (*p == '}')
398                                         p++;
399                         } else if (*p == '*') {
400                                 p++;
401                                 suppress = 1;
402                         } else if (isxdigit(*p)) {
403                                 something = 1;
404                                 value = strtol(p, &p, 16);
405                                 state = START_FIELD;
406                         } else if (tolower(*p) == 'v') {
407                                 p++;
408                                 something = 2;
409                                 value = *value_p;
410                                 state = START_FIELD;
411                         } else if (tolower(*p) == 'i') {
412                                 /*
413                                  * Try to work without the "v".
414                                  */
415                                 something = 2;
416                                 value = *value_p;
417                                 p++;
418
419                                 *fmt = 'i';
420                                 field_size = 8;
421                                 field_width = strtol(p, &p, 10);
422                                 state = DONE;
423
424                         } else if (tolower(*p) == 't') {
425                                 /*
426                                  * XXX: B can't work: Sees the 'b' as a
427                                  * hex digit in "isxdigit".  try "t" for
428                                  * bit field.
429                                  */
430                                 something = 2;
431                                 value = *value_p;
432                                 p++;
433
434                                 *fmt = 'b';
435                                 field_size = 1;
436                                 field_width = strtol(p, &p, 10);
437                                 state = DONE;
438                         } else if (tolower(*p) == 's') {
439                                 /* Seek */
440                                 *fmt = 's';
441                                 p++;
442                                 if (tolower(*p) == 'v') {
443                                         p++;
444                                         something = 2;
445                                         value = *value_p;
446                                 } else {
447                                         something = 1;
448                                         value = strtol(p, &p, 0);
449                                 }
450                                 state = DONE;
451                         } else {
452                                 fprintf(stderr, "Invalid starting "
453                                         "character: %c\n", *p);
454                                 is_error = 1;
455                                 state = DONE;
456                         }
457                         break;
458
459                 case START_FIELD:
460                         if (*p == ':') {
461                                 p++;
462                                 field_size = 1;         /* Default to bits
463                                                            when specified */
464                                 state = GET_FIELD;
465                         } else
466                                 state = DONE;
467                         break;
468
469                 case GET_FIELD:
470                         if (isdigit(*p)) {
471                                 *fmt = 'b';
472                                 field_size = 1;
473                                 field_width = strtol(p, &p, 10);
474                                 state = DONE;
475                         } else if (*p == 'i') {
476
477                                 /* Integral (bytes) */
478                                 p++;
479
480                                 *fmt = 'i';
481                                 field_size = 8;
482                                 field_width = strtol(p, &p, 10);
483                                 state = DONE;
484                         } else if (*p == 'b') {
485
486                                 /* Bits */
487                                 p++;
488
489                                 *fmt = 'b';
490                                 field_size = 1;
491                                 field_width = strtol(p, &p, 10);
492                                 state = DONE;
493                         } else {
494                                 fprintf(stderr, "Invalid startfield %c "
495                                         "(%02x)\n", *p, *p);
496                                 is_error = 1;
497                                 state = DONE;
498                         }
499                         break;
500
501                 case DONE:
502                         break;
503                 }
504         }
505
506         if (is_error) {
507                 *error_p = 1;
508                 return 0;
509         }
510
511         *error_p = 0;
512         *pp = p;
513         *width_p = field_width * field_size;
514         *value_p = value;
515         *suppress_p = suppress;
516
517         return (something);
518 }
519
520 static int
521 do_encode(u_char *buff, size_t vec_max, size_t *used,
522           int (*arg_get)(void *, char *), void *gethook, char *fmt, va_list ap)
523 {
524         int ind;
525         int shift;
526         u_char val;
527         int ret;
528         int width, value, error, suppress;
529         char c;
530         int encoded = 0;
531         char field_name[80];
532
533         ind = 0;
534         shift = 0;
535         val = 0;
536
537         while ((ret = next_field(&fmt, &c, &width, &value, field_name,
538                                  sizeof(field_name), &error, &suppress))) {
539                 encoded++;
540
541                 if (ret == 2) {
542                         if (suppress)
543                                 value = 0;
544                         else
545                                 value = arg_get ?
546                                         (*arg_get)(gethook, field_name) :
547                                         va_arg(ap, int);
548                 }
549
550 #if 0
551                 printf(
552 "do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
553                 ret, c, width, value, field_name, error, suppress);
554 #endif
555                 /* Absolute seek */
556                 if (c == 's') {
557                         ind = value;
558                         continue;
559                 }
560
561                 /* A width of < 8 is a bit field. */
562                 if (width < 8) {
563
564                         /* This is a bit field.  We start with the high bits
565                          * so it reads the same as the SCSI spec.
566                          */
567
568                         shift += width;
569
570                         val |= (value << (8 - shift));
571
572                         if (shift == 8) {
573                                 if (ind < vec_max) {
574                                         buff[ind++] = val;
575                                         val = 0;
576                                 }
577                                 shift = 0;
578                         }
579                 } else {
580                         if (shift) {
581                                 if (ind < vec_max) {
582                                         buff[ind++] = val;
583                                         val = 0;
584                                 }
585                                 shift = 0;
586                         }
587                         switch(width) {
588                         case 8:         /* 1 byte integer */
589                                 if (ind < vec_max)
590                                         buff[ind++] = value;
591                                 break;
592
593                         case 16:        /* 2 byte integer */
594                                 if (ind < vec_max - 2 + 1) {
595                                         buff[ind++] = value >> 8;
596                                         buff[ind++] = value;
597                                 }
598                                 break;
599
600                         case 24:        /* 3 byte integer */
601                                 if (ind < vec_max - 3 + 1) {
602                                         buff[ind++] = value >> 16;
603                                         buff[ind++] = value >> 8;
604                                         buff[ind++] = value;
605                                 }
606                                 break;
607
608                         case 32:        /* 4 byte integer */
609                                 if (ind < vec_max - 4 + 1) {
610                                         buff[ind++] = value >> 24;
611                                         buff[ind++] = value >> 16;
612                                         buff[ind++] = value >> 8;
613                                         buff[ind++] = value;
614                                 }
615                                 break;
616
617                         default:
618                                 fprintf(stderr, "do_encode: Illegal width\n");
619                                 break;
620                         }
621                 }
622         }
623
624         /* Flush out any remaining bits
625          */
626         if (shift && ind < vec_max) {
627                 buff[ind++] = val;
628                 val = 0;
629         }
630
631
632         if (used)
633                 *used = ind;
634
635         if (error)
636                 return -1;
637
638         return encoded;
639 }
640
641 int
642 csio_decode(struct ccb_scsiio *csio, char *fmt, ...)
643 {
644         va_list ap;
645
646         va_start(ap, fmt);
647
648         return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
649                               0, 0, fmt, ap));
650 }
651
652 int
653 csio_decode_visit(struct ccb_scsiio *csio, char *fmt,
654                   void (*arg_put)(void *, int, void *, int, char *),
655                   void *puthook)
656 {
657         va_list ap;
658
659         /*
660          * We need some way to output things; we can't do it without
661          * the arg_put function.
662          */
663         if (arg_put == NULL)
664                 return(-1);
665
666         bzero(&ap, sizeof(ap));
667
668         return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
669                               arg_put, puthook, fmt, ap));
670 }
671
672 int
673 buff_decode(u_int8_t *buff, size_t len, char *fmt, ...)
674 {
675         va_list ap;
676
677         va_start(ap, fmt);
678
679         return(do_buff_decode(buff, len, 0, 0, fmt, ap));
680 }
681
682 int
683 buff_decode_visit(u_int8_t *buff, size_t len, char *fmt,
684                   void (*arg_put)(void *, int, void *, int, char *),
685                   void *puthook)
686 {
687         va_list ap;
688
689         /*
690          * We need some way to output things; we can't do it without
691          * the arg_put function.
692          */
693         if (arg_put == NULL)
694                 return(-1);
695
696         bzero(&ap, sizeof(ap));
697
698         return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap));
699 }
700
701 /*
702  * Build a SCSI CCB, given the command and data pointers and a format
703  * string describing the 
704  */
705 int
706 csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
707            u_int32_t flags, int retry_count, int timeout, char *cmd_spec, ...)
708 {
709         size_t cmdlen;
710         int retval;
711         va_list ap;
712
713         if (csio == NULL)
714                 return(0);
715
716         bzero(csio, sizeof(struct ccb_scsiio));
717
718         va_start(ap, cmd_spec);
719
720         if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
721                                 &cmdlen, NULL, NULL, cmd_spec, ap)) == -1)
722                 return(retval);
723
724         cam_fill_csio(csio,
725                       /* retries */ retry_count,
726                       /* cbfcnp */ NULL,
727                       /* flags */ flags,
728                       /* tag_action */ MSG_SIMPLE_Q_TAG,
729                       /* data_ptr */ data_ptr,
730                       /* dxfer_len */ dxfer_len,
731                       /* sense_len */ SSD_FULL_SIZE,
732                       /* cdb_len */ cmdlen,
733                       /* timeout */ timeout ? timeout : 5000);
734
735         return(retval);
736 }
737
738 int
739 csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
740                  u_int32_t dxfer_len, u_int32_t flags, int retry_count,
741                  int timeout, char *cmd_spec,
742                  int (*arg_get)(void *hook, char *field_name), void *gethook)
743 {
744         va_list ap;
745         size_t cmdlen;
746         int retval;
747
748         if (csio == NULL)
749                 return(0);
750
751         /*
752          * We need something to encode, but we can't get it without the
753          * arg_get function.
754          */
755         if (arg_get == NULL)
756                 return(-1);
757
758         bzero(&ap, sizeof(ap));
759
760         bzero(csio, sizeof(struct ccb_scsiio));
761
762         if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
763                                 &cmdlen, arg_get, gethook, cmd_spec, ap)) == -1)
764                 return(retval);
765
766         cam_fill_csio(csio,
767                       /* retries */ retry_count,
768                       /* cbfcnp */ NULL,
769                       /* flags */ flags,
770                       /* tag_action */ MSG_SIMPLE_Q_TAG,
771                       /* data_ptr */ data_ptr,
772                       /* dxfer_len */ dxfer_len,
773                       /* sense_len */ SSD_FULL_SIZE,
774                       /* cdb_len */ cmdlen,
775                       /* timeout */ timeout ? timeout : 5000);
776
777         return(retval);
778 }
779
780 int
781 csio_encode(struct ccb_scsiio *csio, char *fmt, ...)
782 {
783         va_list ap;
784
785         if (csio == NULL)
786                 return(0);
787
788         va_start(ap, fmt);
789
790         return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap));
791 }
792
793 int
794 buff_encode_visit(u_int8_t *buff, size_t len, char *fmt,
795                   int (*arg_get)(void *hook, char *field_name), void *gethook)
796 {
797         va_list ap;
798
799         /*
800          * We need something to encode, but we can't get it without the
801          * arg_get function.
802          */
803         if (arg_get == NULL)
804                 return(-1);
805
806         bzero(&ap, sizeof(ap));
807
808         return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap));
809 }
810
811 int
812 csio_encode_visit(struct ccb_scsiio *csio, char *fmt,
813                   int (*arg_get)(void *hook, char *field_name), void *gethook)
814 {
815         va_list ap;
816
817         /*
818          * We need something to encode, but we can't get it without the
819          * arg_get function.
820          */
821         if (arg_get == NULL)
822                 return(-1);
823
824         bzero(&ap, sizeof(ap));
825
826         return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
827                          gethook, fmt, ap));
828 }