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