1 /* SLLIB.C (c) Copyright Leland Lucius, 2000-2009 */
2 /* Library for managing Standard Label tapes */
3
4 /*
5 || ----------------------------------------------------------------------------
6 ||
7 || SLLIB.C (c) Copyright Leland Lucius, 2000-2009
8 || Released under terms of the Q Public License.
9 ||
10 || Library for managing Standard Label tapes.
11 ||
12 || ----------------------------------------------------------------------------
13 */
14
15 #include "hstdinc.h"
16
17 #define _SLLIB_C_
18 #define _HTAPE_DLL_
19
20 #include "hercules.h"
21 #include "sllib.h"
22
23 /*
24 || Local constant data
25 */
26
27 /*
28 || Label IDs in EBCDIC
29 */
30 static const char *sl_elabs[] =
31 {
32 "\x00\x00\x00", /* Placeholder */
33 "\xE5\xD6\xD3", /* EBCDIC characters "VOL" */
34 "\xC8\xC4\xD9", /* EBCDIC characters "HDR" */
35 "\xE4\xC8\xD3", /* EBCDIC characters "UHL" */
36 "\xC5\xD6\xC6", /* EBCDIC characters "EOF" */
37 "\xC5\xD6\xE5", /* EBCDIC characters "EOV" */
38 "\xE4\xE3\xD3", /* EBCDIC characters "UTL" */
39 };
40 #define SL_ELABS_MAX ( sizeof( sl_elabs ) / sizeof( sl_elabs[ 0 ] ) )
41
42 /*
43 || Label IDs in ASCII
44 */
45 static const char *sl_alabs[] =
46 {
47 "\x00\x00\x00", /* Placeholder */
48 "\x56\x4f\x4c", /* ASCII characters "VOL" */
49 "\x48\x44\x52", /* ASCII characters "HDR" */
50 "\x55\x48\x4c", /* ASCII characters "UHL" */
51 "\x45\x4f\x46", /* ASCII characters "EOF" */
52 "\x45\x4f\x56", /* ASCII characters "EOV" */
53 "\x55\x54\x4c", /* ASCII characters "UTL" */
54 };
55 #define SL_ALABS_MAX ( sizeof( sl_alabs ) / sizeof( sl_alabs[ 0 ] ) )
56
57 /*
58 || Minimum and maximum ranges for each label type
59 */
60 static const struct
61 {
62 int min;
63 int max;
64 }
65 sl_ranges[] =
66 {
67 { 0, 0 }, /* Placeholder */
68 { 1, 1 }, /* ASCII characters "VOL" */
69 { 1, 2 }, /* ASCII characters "HDR" */
70 { 1, 8 }, /* ASCII characters "UHL" */
71 { 1, 2 }, /* ASCII characters "EOF" */
72 { 1, 2 }, /* ASCII characters "EOV" */
73 { 1, 8 }, /* ASCII characters "UTL" */
74 };
75
76 /*
77 || Text descriptions for errors
78 */
79 static const char *sl_errstr[] =
80 {
81 "No error",
82 "Block size out of range",
83 "Data set sequence out of range",
84 "Invalid expiration date",
85 "Missing or invalid job name",
86 "Missing or invalid record length",
87 "Owner string invalid or too long",
88 "Missing or invalid record format",
89 "Missing or invalid step name",
90 "Invalid recording technique",
91 "Volume sequence out of range",
92 "Missing or invalid volume serial",
93 "User data too long",
94 "Label type invalid",
95 "Label number invalid",
96 "Invalid error code",
97 };
98 #define SL_ERRSTR_MAX ( sizeof( sl_errstr) / sizeof( sl_errstr[ 0 ] ) )
99
100 /*
101 || Valid characters for a Standard Label
102 || (from: SC26-4565-01 "3.4 Label Definition and Organization")
103 */
104 static const char
105 sl_cset[] =
106 {
107 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 !\"%&'()*+,-./:;<=>?"
108 };
109
110 /*
111 || Valid record formats
112 */
113 static const struct
114 {
115 char *recfm;
116 char f;
117 char b;
118 char c;
119 }
120 valfm[] =
121 {
122 { "U", 'U', ' ', ' ' },
123 { "UA", 'U', ' ', 'A' },
124 { "UM", 'U', ' ', 'M' },
125 { "F", 'F', ' ', ' ' },
126 { "FA", 'F', ' ', 'A' },
127 { "FM", 'F', ' ', 'M' },
128 { "FB", 'F', 'B', ' ' },
129 { "FBA", 'F', 'B', 'A' },
130 { "FBM", 'F', 'B', 'M' },
131 { "FS", 'F', 'S', ' ' },
132 { "FSA", 'F', 'S', 'A' },
133 { "FSM", 'F', 'S', 'M' },
134 { "FBS", 'F', 'R', ' ' },
135 { "FBSA", 'F', 'R', 'A' },
136 { "FBSM", 'F', 'R', 'M' },
137 { "V", 'V', ' ', ' ' },
138 { "VA", 'V', ' ', 'A' },
139 { "VM", 'V', ' ', 'M' },
140 { "VB", 'V', 'B', ' ' },
141 { "VBA", 'V', 'B', 'A' },
142 { "VBM", 'V', 'B', 'M' },
143 { "VS", 'V', 'S', ' ' },
144 { "VSA", 'V', 'S', 'A' },
145 { "VSM", 'V', 'S', 'M' },
146 { "VBS", 'V', 'R', ' ' },
147 { "VBSA", 'V', 'R', 'A' },
148 { "VBSM", 'V', 'R', 'M' },
149 };
150 #define VALFMCNT ( sizeof( valfm ) / sizeof( valfm[ 0 ] ) )
151
152 /*==DOC==
153
154 NAME
155 sl_atoe - Translate input buffer from ASCII to EBCDIC
156
157 SYNOPSIS
158 #include "sllib.h"
159
160 char *sl_atoe( void *dbuf, void *sbuf, int slen )
161
162 DESCRIPTION
163 Translates, and optionally copies, "sbuf" from ASCII to
164 EBCDIC for "slen" characters.
165
166 If "dbuf" is specified as NULL, then "sbuf" is translated in
167 place. Otherwise, "dbuf" specifies the buffer where the
168 translated characters will be stored.
169
170 RETURN VALUE
171 The return value will be either "sbuf" or "dbuf" depending on
172 whether "dbuf" was passed as NULL.
173
174 EXAMPLE
175 //
176 // Convert buffer
177 //
178
179 #include "sllib.h"
180
181 unsigned char ascii[] =
182 "\x48\x65\x72\x63\x75\x6c\x65\x73\x2e\x2e\x2e"
183 "\x20\x52\x65\x73\x75\x72\x72\x65\x63\x74\x69"
184 "\x6e\x67\x20\x74\x68\x65\x20\x64\x69\x6e\x6f"
185 "\x73\x61\x75\x72\x73\x21";
186 unsigned char ebcdic[ sizeof( ascii ) ];
187
188 int main( int argc, char *argv[] )
189 {
190 int len;
191 int i;
192
193 len = strlen( ascii );
194
195 sl_atoe( ebcdic, ascii, len );
196
197 printf( "ascii string: " );
198
199 for( i = 0 ; i < len ; i++ )
200 {
201 printf( "%02x ", ascii[ i ] );
202 }
203
204 printf( "\nebcdic string: " );
205
206 for( i = 0 ; i < len ; i++ )
207 {
208 printf( "%02x ", ebcdic[ i ] );
209 }
210
211 printf( "\n" );
212
213 return( 0 );
214 }
215
216 SEE ALSO
217
218 ==DOC==*/
219
220 DLL_EXPORT
221 char *
sl_atoe(void * dbuf,void * sbuf,int slen)222 sl_atoe( void *dbuf, void *sbuf, int slen )
223 {
224 unsigned char *sptr;
225 unsigned char *dptr;
226
227 sptr = sbuf;
228 dptr = dbuf;
229
230 if( dptr == NULL )
231 {
232 dptr = sptr;
233 }
234
235 while( slen > 0 )
236 {
237 slen--;
238 dptr[ slen ] = host_to_guest( sptr[ slen ] );
239 }
240
241 return( (char *)dptr );
242 }
243
244 /*==DOC==
245
246 NAME
247 sl_etoa - Translate input buffer from EBCDIC to ASCII
248
249 SYNOPSIS
250 #include "sllib.h"
251
252 char *sl_etoa( void *dbuf, void *sbuf, int slen )
253
254 DESCRIPTION
255 Translates, and optionally copies, "sbuf" from EBCDIC to
256 ASCII for "slen" characters.
257
258 If "dbuf" is specified as NULL, then "sbuf" is translated in
259 place. Otherwise, "dbuf" specifies the buffer where the
260 translated characters will be stored.
261
262 RETURN VALUE
263 The return value will be either "sbuf" or "dbuf" depending on
264 whether "dbuf" was passed as NULL.
265
266 EXAMPLE
267 //
268 // Convert buffer
269 //
270
271 #include "sllib.h"
272
273 unsigned char ebcdic[] =
274 "\xc8\x85\x99\x83\xa4\x93\x85\xa2\x4b\x4b\x4b"
275 "\x40\xd9\x85\xa2\xa4\x99\x99\x85\x83\xa3\x89"
276 "\x95\x87\x40\xa3\x88\x85\x40\x84\x89\x95\x96"
277 "\xa2\x81\xa4\x99\xa2\x5a";
278 unsigned char ascii[ sizeof( ebcdic ) ];
279
280 int main( int argc, char *argv[] )
281 {
282 int len;
283 int i;
284
285 len = strlen( ebcdic );
286
287 sl_etoa( ascii, ebcdic, len );
288
289 printf( "ebcdic string: " );
290
291 for( i = 0 ; i < len ; i++ )
292 {
293 printf( "%02x ", ebcdic[ i ] );
294 }
295
296 printf( "\nascii string: " );
297
298 for( i = 0 ; i < len ; i++ )
299 {
300 printf( "%02x ", ascii[ i ] );
301 }
302
303 printf( "\n" );
304
305 return( 0 );
306 }
307
308 SEE ALSO
309
310 ==DOC==*/
311
312 DLL_EXPORT
313 char *
sl_etoa(void * dbuf,void * sbuf,int slen)314 sl_etoa( void *dbuf, void *sbuf, int slen )
315 {
316 unsigned char *sptr;
317 unsigned char *dptr;
318
319 sptr = sbuf;
320 dptr = dbuf;
321
322 if( dptr == NULL )
323 {
324 dptr = sptr;
325 }
326
327 while( slen > 0 )
328 {
329 slen--;
330 dptr[ slen ] = guest_to_host( sptr[ slen ] );
331 }
332
333 return( (char *)dptr );
334 }
335
336 /*==DOC==
337
338 NAME
339 sl_islabel - Determines if passed data represents a standard label
340
341 SYNOPSIS
342 #include "sllib.h"
343
344 int sl_islabel( SLLABEL *dlab, void *buf, int len )
345
346 DESCRIPTION
347 This function performs several tests to determine if the "buf"
348 parameter points to a valid standard label. The "len" parameter
349 must be the length of the data pointed to by the "buf" parameter.
350 If "dlab" does not contain NULL, then the data pointed to by "buf"
351 will be converted to ASCII and placed at the "dlab" location.
352
353 RETURN VALUE
354 TRUE is returned if the data appears to be a standard label.
355 Otherwise, FALSE is returned.
356
357 NOTES
358 The input label may be in either ASCII or EBCDIC.
359
360 Currently, the tests are quite trival, but they may become more
361 strict.
362
363 EXAMPLE
364 //
365 // Test validity of a label
366 //
367
368 #include "sllib.h"
369
370 int main( int argc, char *argv[] )
371 {
372 SLLABEL sllab = { 0 };
373
374 printf( "Label is%s valid\n",
375 ( sl_islabel( NULL, &sllab, sizeof( sllab ) ? "" : " not" ) );
376
377 sl_vol1( &sllab, "HET001", "HERCULES" );
378
379 printf( "Label is: %s valid\n",
380 ( sl_islabel( NULL, &sllab, sizeof( sllab ) ? "" : " not" ) );
381
382 return( 0 );
383 }
384
385 SEE ALSO
386 sl_vol1()
387
388 ==DOC==*/
389
390 DLL_EXPORT
391 int
sl_islabel(SLLABEL * lab,void * buf,int len)392 sl_islabel( SLLABEL *lab, void *buf, int len )
393 {
394 int i;
395 int num;
396 unsigned char *ptr;
397
398 if( len != sizeof( SLLABEL ) )
399 {
400 return FALSE;
401 }
402
403 for( i = 1 ; i < (int)SL_ELABS_MAX ; i++ )
404 {
405 if( memcmp( sl_elabs[ i ], buf, 3 ) == 0 )
406 {
407 ptr = buf;
408 num = ptr[ 3 ] - (unsigned char) '\xF0';
409 if( ( num >= sl_ranges[ i ].min ) && ( num <= sl_ranges[ i ].max ) )
410 {
411 if( lab != NULL )
412 {
413 sl_etoa( lab, buf, len );
414 }
415 return( TRUE );
416 }
417 }
418
419 if( memcmp( sl_alabs[ i ], buf, 3 ) == 0 )
420 {
421 ptr = buf;
422 num = ptr[ 3 ] - (unsigned char) '\x30';
423 if( ( num >= sl_ranges[ i ].min ) && ( num <= sl_ranges[ i ].max ) )
424 {
425 if( lab != NULL )
426 {
427 memcpy( lab, buf, len );
428 }
429
430 return( TRUE );
431 }
432 }
433 }
434
435 return( FALSE );
436 }
437
438 /*==DOC==
439
440 NAME
441 sl_istype - Verifies data is of specified standard label type
442
443 SYNOPSIS
444 #include "sllib.h"
445
446 int sl_istype( void *buf, int type, int num )
447
448 DESCRIPTION
449 This function verifies that the data pointed to by the "buf"
450 parameter contains a standard label as determined by the "type"
451 and "num" parameters.
452
453 The "type" parameter can be one of the "SLT_*" defines found in
454 the "sllib.h" header file.
455
456 The "num" parameter further defines the type and is usually 1
457 or 2. However, 0 may be specified to only test using the "type"
458 parameter.
459
460 RETURN VALUE
461 TRUE is returned if the data is of the given type. Otherwise,
462 FALSE is returned.
463
464 NOTES
465 The input data may be in either ASCII or EBCDIC.
466
467 This routine is "usually" not called directly by user programs.
468 The "sl_is*" macros in "sllib.h" should be used instead.
469
470 EXAMPLE
471 //
472 // Determine if data is a VOL1 or HDR2 label.
473 //
474
475 #include "sllib.h"
476
477 int main( int argc, char *argv[] )
478 {
479 SLLABEL sllab = { 0 };
480
481 sl_vol1( &sllab, "HET001", "HERCULES" );
482
483 printf( "Label is%s a VOL1\n",
484 ( sl_istype( &sllab, SLT_VOL, 1 ) ? "" : " not" ) );
485
486 printf( "Label is%s a HDR2\n",
487 ( sl_istype( &sllab, SLT_HDR, 2 ) ? "" : " not" ) );
488
489 return( 0 );
490 }
491
492 SEE ALSO
493 sl_vol1()
494
495 ==DOC==*/
496
497 DLL_EXPORT
498 int
sl_istype(void * buf,int type,int num)499 sl_istype( void *buf, int type, int num )
500 {
501 unsigned char *ptr;
502
503 ptr = buf;
504
505 /*
506 || Check EBCDIC table
507 */
508 if( memcmp( buf, sl_elabs[ type ], 3 ) == 0 )
509 {
510 if( ( num == 0 ) || ( ptr[ 3 ] == ( ( (unsigned char) '\xF0' ) + num ) ) )
511 {
512 return( TRUE );
513 }
514 }
515
516 /*
517 || Check ASCII table
518 */
519 if( memcmp( buf, sl_alabs[ type ], 3 ) == 0 )
520 {
521 if( ( num == 0 ) || ( ptr[ 3 ] == ( ( (unsigned char) '\x30') + num ) ) )
522 {
523 return( TRUE );
524 }
525 }
526
527 return( FALSE );
528 }
529
530 /*==DOC==
531
532 NAME
533 sl_fmtdate - Converts dates to/from SL format
534
535 SYNOPSIS
536 #include "sllib.h"
537
538 char *sl_fmtdate( char *dest, char *src, int fromto )
539
540 DESCRIPTION
541 Converts the "src" date from or to the SL format and places the
542 result at the "dest" location. If the "src" parameter is specified
543 as NULL, then the current date will automatically be supplied.
544
545 The "fromto" parameter controls the type of conversion. Specify
546 FALSE to convert to SL format and TRUE from convert from SL format.
547
548 When converting to the SL format, the "src" parameter must contain
549 a valid Julian date in one of the following formats:
550 YYDDD
551 YY.DDD
552 YYYYDDD
553 YYYY.DDD
554
555 RETURN VALUE
556 If "src" contains an invalid date, then NULL will be returned.
557 Otherwise, the "dest" value is returned.
558
559 EXAMPLE
560 //
561 // Convert julian date to SL format
562 //
563
564 #include "sllib.h"
565
566 char sldate[ SL_DATELEN ];
567 char jdate[] = "1998.212";
568
569 int main( int argc, char *argv[] )
570 {
571
572 sl_fmtdate( sldate, jdate, FALSE );
573
574 printf( "Julian date : %s\n", jdate );
575 printf( "SL date : %-6.6s\n", sldate );
576
577 return( 0 );
578 }
579
580 SEE ALSO
581
582 ==DOC==*/
583
584 DLL_EXPORT
585 char *
sl_fmtdate(char * dest,char * src,int fromto)586 sl_fmtdate( char *dest, char *src, int fromto )
587 {
588 char wbuf[ 9 ];
589 char sbuf[ 9 ];
590 char *ptr;
591 time_t curtime;
592 struct tm tm;
593 int ret;
594
595 /*
596 || If source represents an SL date, then convert it to julian
597 */
598 if( fromto )
599 {
600 if( src == NULL )
601 {
602 return( NULL );
603 }
604
605 if( src[ 5 ] == '0' )
606 {
607 dest[ 0 ] = src[ 1 ];
608 dest[ 1 ] = src[ 2 ];
609 }
610 else if( src[ 0 ] == ' ' )
611 {
612 dest[ 0 ] = '1';
613 dest[ 1 ] = '9';
614 }
615 else
616 {
617 dest[ 0 ] = '2';
618 dest[ 1 ] = src[ 0 ];
619 }
620
621 memcpy( &dest[ 2 ], &src[ 1 ] , 2 );
622 dest[ 4 ] = '.';
623 memcpy( &dest[ 5 ], &src[ 3 ] , 3 );
624 }
625 else
626 {
627 /*
628 || Supply current date if source is null
629 */
630 if( src == NULL )
631 {
632 strftime( sbuf, sizeof( sbuf ), "%Y%j", localtime( &curtime ) );
633 src = sbuf;
634 }
635
636 /*
637 || Base initial guess at format on length of src date
638 */
639 switch( strlen( src ) )
640 {
641 case 5:
642 ptr = "%2u%3u";
643 break;
644
645 case 6:
646 ptr = "%2u.%3u";
647 break;
648
649 case 7:
650 ptr = "%4u%3u";
651 break;
652
653 case 8:
654 ptr = "%4u.%3u";
655 break;
656
657 default:
658 return( NULL );
659 break;
660 }
661
662 /*
663 || Convert src to "tm" format
664 */
665 ret = sscanf( src, ptr, &tm.tm_year, &tm.tm_yday );
666 if( ret != 2 || tm.tm_yday < 1 || tm.tm_yday > 366 )
667 {
668 return( NULL );
669 }
670 tm.tm_yday--;
671
672 /*
673 || Now, convert to SL tape format
674 */
675 strftime( wbuf, sizeof( wbuf ), "%Y%j", &tm );
676 if( tm.tm_year < 100 )
677 {
678 /*
679 || 1900s are indicated by a blank.
680 */
681 wbuf[ 1 ] = ' ';
682 }
683
684 /*
685 || Finally, copy SL date to destination
686 */
687 memcpy( dest, &wbuf[ 1 ], 6 );
688 }
689
690 /*
691 || Return dest pointer
692 */
693 return( dest );
694 }
695
696 /*==DOC==
697
698 NAME
699 sl_fmtlab - Transforms an SL label from raw to cooked format
700
701 SYNOPSIS
702 #include "sllib.h"
703
704 void sl_fmtlab( SLFMT *fmt, SLLABEL *lab )
705
706 DESCRIPTION
707 Converts the SL label specified by "lab" into a "cooked" format
708 that's easier to process. Text descriptions are supplied for
709 each field are also supplied.
710
711 RETURN VALUE
712 Nothing is returned.
713
714 NOTES
715 The input label may be in either ASCII or EBCDIC. It will be
716 converted to ASCII before building the cooked version.
717
718 The first two fields of the SLFMT structure are arrays that contain
719 the text description and value for each field based on the label
720 type. The arrays are terminated with NULL pointers.
721
722 EXAMPLE
723 //
724 // Convert an SL label to cooked format
725 //
726
727 #include "sllib.h"
728
729 int main( int argc, char *argv[] )
730 {
731 SLFMT slfmt;
732 SLLABEL sllab;
733 int i;
734
735 sl_vol1( &sllab, "HET001", "HERCULES" );
736
737 sl_fmtlab( &slfmt, &sllab );
738
739 for( i = 0 ; slfmt.key[ i ] != NULL ; i++ )
740 {
741 printf("%-20.20s: '%s'\n", slfmt.key[ i ] , slfmt.val[ i ] );
742 }
743
744 return( 0 );
745 }
746
747 SEE ALSO
748 sl_vol1()
749
750 ==DOC==*/
751
752 #define lab2fmt( i1, f2, l3, k4 ) \
753 fmt->key[ i1 ] = k4; \
754 fmt->val[ i1 ] = fmt->f2; \
755 memcpy( fmt->f2, lab->f2, l3 );
756 DLL_EXPORT
757 void
sl_fmtlab(SLFMT * fmt,SLLABEL * lab)758 sl_fmtlab( SLFMT *fmt, SLLABEL *lab )
759 {
760 SLLABEL templab;
761
762 /*
763 || Initialize
764 */
765 memset( fmt, 0, sizeof( SLFMT ) );
766
767 /*
768 || If label appears to be EBCDIC, convert to ASCII before processing
769 */
770 if( sl_islabel( &templab, lab, sizeof( SLLABEL ) ) == FALSE )
771 {
772 return;
773 }
774 lab = &templab;
775
776 /*
777 || Store label type (combine ID and NUM)
778 */
779 fmt->key[ 0 ] = "Label";
780 fmt->val[ 0 ] = fmt->type;
781 memcpy( fmt->type, lab->id, 4 );
782
783 /*
784 || Build remaining fields based on label type
785 */
786 if( memcmp( lab->id, "VOL", 3 ) == 0 )
787 {
788 if( lab->num[ 0 ] == '1' )
789 {
790 lab2fmt( 1, slvol.volser, 6, "Volume Serial" );
791 lab2fmt( 2, slvol.idrc, 1, "Improved Data Rec." );
792 lab2fmt( 3, slvol.owner, 10, "Owner Code" );
793 }
794 }
795 else if( ( memcmp( lab->id, "HDR", 3 ) == 0 ) ||
796 ( memcmp( lab->id, "EOF", 3 ) == 0 ) ||
797 ( memcmp( lab->id, "EOV", 3 ) == 0 ) )
798 {
799 if( lab->num[ 0 ] == '1' )
800 {
801 lab2fmt( 1, slds1.dsid, 17, "Dataset ID" );
802 lab2fmt( 2, slds1.volser, 6, "Volume Serial" );
803 lab2fmt( 3, slds1.volseq, 4, "Volume Sequence" );
804 lab2fmt( 4, slds1.dsseq, 4, "Dataset Sequence" );
805 lab2fmt( 5, slds1.genno, 4, "GDG Number" );
806 lab2fmt( 6, slds1.verno, 2, "GDG Version" );
807 lab2fmt( 7, slds1.crtdt, 6, "Creation Date" );
808 lab2fmt( 8, slds1.expdt, 6, "Expiration Date" );
809 lab2fmt( 9, slds1.dssec, 1, "Dataset Security" );
810 lab2fmt( 10, slds1.blklo, 6, "Block Count Low" );
811 lab2fmt( 11, slds1.syscd, 13, "System Code" );
812 lab2fmt( 12, slds1.blkhi, 4, "Block Count High" );
813 }
814 else if( lab->num[ 0 ] == '2' )
815 {
816 lab2fmt( 1, slds2.recfm, 1, "Record Format" );
817 lab2fmt( 2, slds2.blksize, 5, "Block Size" );
818 lab2fmt( 3, slds2.lrecl, 5, "Record Length" );
819 lab2fmt( 4, slds2.den, 1, "Density" );
820 lab2fmt( 5, slds2.dspos, 1, "Dataset Position" );
821 lab2fmt( 6, slds2.jobid, 17, "Job/Step ID" );
822 lab2fmt( 7, slds2.trtch, 2, "Recording Technique" );
823 lab2fmt( 8, slds2.ctrl, 1, "Control Character" );
824 lab2fmt( 9, slds2.blkattr, 1, "Block Attribute" );
825 lab2fmt( 10, slds2.devser, 6, "Device Serial" );
826 lab2fmt( 11, slds2.ckptid, 1, "Checkpoint ID" );
827 lab2fmt( 12, slds2.lblkln, 10, "Large Block Length" );
828 }
829 }
830 else if( memcmp( lab->id, "USR", 3 ) == 0 )
831 {
832 lab2fmt( 1, slusr.data, 76, "User Data" );
833 }
834
835 return;
836 }
837 #undef lab2fmt
838
839 /*==DOC==
840
841 NAME
842 sl_vol - Generate a volume label
843
844 SYNOPSIS
845 #include "sllib.h"
846
847 int sl_vol( SLLABEL *lab,
848 char *volser,
849 char *owner )
850
851 DESCRIPTION
852 This function builds a volume label based on the parameters
853 provided and places it at the location pointed to by the "lab"
854 parameter in EBCDIC.
855
856 The remaining parameters correspond to fields within the label
857 and are converted to EBCDIC before storing.
858
859 The "owner" parameter may be specified as NULL, in which case
860 blanks are supplied.
861
862 RETURN VALUE
863 The return value will be >= 0 if no errors are detected.
864
865 If an error is detected, then the return value will be < 0 and
866 will be one of the following:
867
868 SLE_VOLSER Missing or invalid volume serial
869 SLE_OWNER Owner string too long
870
871 NOTES
872 This routine is normally accessed using the supplied "sl_vol1"
873 macro.
874
875 Only the "most common" label fields have corresponding parameters
876 so the user must supply any other desired values.
877
878 EXAMPLE
879 //
880 // Create a VOL1 label
881 //
882
883 #include "sllib.h"
884
885 int main( int argc, char *argv[] )
886 {
887 SLFMT slfmt;
888 SLLABEL sllab;
889 int i;
890
891 sl_vol( &sllab, "HET001", "HERCULES" );
892
893 sl_fmtlab( &slfmt, &sllab );
894
895 for( i = 0 ; slfmt.key[ i ] != NULL ; i++ )
896 {
897 printf("%-20.20s: '%s'\n", slfmt.key[ i ] , slfmt.val[ i ] );
898 }
899
900 return( 0 );
901 }
902
903 SEE ALSO
904 sl_fmtlab()
905
906 ==DOC==*/
907
908 DLL_EXPORT
909 int
sl_vol(SLLABEL * lab,char * volser,char * owner)910 sl_vol( SLLABEL *lab,
911 char *volser,
912 char *owner )
913 {
914 size_t len;
915
916 /*
917 || Initialize
918 */
919 memset( lab, ' ', sizeof( SLLABEL ) );
920
921 /*
922 || Label ID
923 */
924 memcpy( lab->id, sl_alabs[ SLT_VOL ], 3 );
925
926 /*
927 || Label number
928 */
929 lab->num[ 0 ] = '1';
930
931 /*
932 || Volser
933 */
934 if( volser == NULL )
935 {
936 return( SLE_VOLSER );
937 }
938
939 len = strlen( volser );
940 if( ( len > 6 ) || ( strspn( volser, sl_cset ) != len ) )
941 {
942 return( SLE_VOLSER );
943 }
944
945 memcpy( lab->slvol.volser, volser, len );
946
947 /*
948 || Owner
949 */
950 if( owner != NULL )
951 {
952 len = strlen( owner );
953 if( len > 10 )
954 {
955 return( SLE_OWNER );
956 }
957 memcpy( lab->slvol.owner, owner, len );
958 }
959
960 /*
961 || Convert to EBCDIC
962 */
963 sl_atoe( NULL, lab, sizeof( SLLABEL ) );
964
965 return 0;
966 }
967
968 /*==DOC==
969
970 NAME
971 sl_ds1 - Generate a data set label 1
972
973 SYNOPSIS
974 #include "sllib.h"
975
976 int sl_ds1( SLLABEL *lab,
977 int type,
978 char *dsn,
979 char *volser,
980 int volseq,
981 int dsseq,
982 char *expdt,
983 int blocks )
984
985 DESCRIPTION
986 This function builds a data set label 1 based on the parameters
987 provided and places it at the location pointed to by the "lab"
988 parameter in EBCDIC.
989
990 The "type" parameter must be "SLT_HDR", "SLT_EOF", or "SLT_EOV".
991
992 The remaining parameters correspond to fields within the label
993 and are converted to EBCDIC before storing.
994
995 The "dsn" parameter may be set to "SL_INITDSN" if "SLT_HDR" is
996 specified for the "type" parameter. This will create an IEHINITT
997 format HDR1 label.
998
999 The "blocks" parameter is forced to 0 for "SLT_HDR" types.
1000
1001 RETURN VALUE
1002 The return value will be >= 0 if no errors are detected.
1003
1004 If an error is detected, then the return value will be < 0 and
1005 will be one of the following:
1006
1007 SLE_INVALIDTYPE Invalid label type specified
1008 SLE_VOLSER Missing or invalid volume serial
1009 SLE_OWNER Owner string too long
1010
1011 NOTES
1012 This routine is normally accessed using the supplied "sl_hdr1",
1013 "sl_eof1", or "sl_eov1" macros.
1014
1015 Only the "most common" label fields have corresponding parameters
1016 so the user must supply any other desired values.
1017
1018 EXAMPLE
1019 //
1020 // Create a EOF1 label
1021 //
1022
1023 #include "sllib.h"
1024
1025 int main( int argc, char *argv[] )
1026 {
1027 SLFMT slfmt;
1028 SLLABEL sllab;
1029 int i;
1030
1031 sl_ds1( &sllab,
1032 SLT_EOF,
1033 "HERCULES.TAPE.G0010V00",
1034 "HERC01",
1035 1,
1036 1,
1037 "2001.321",
1038 289 );
1039
1040 sl_fmtlab( &slfmt, &sllab );
1041
1042 for( i = 0 ; slfmt.key[ i ] != NULL ; i++ )
1043 {
1044 printf("%-20.20s: '%s'\n", slfmt.key[ i ] , slfmt.val[ i ] );
1045 }
1046
1047 return( 0 );
1048 }
1049
1050 SEE ALSO
1051 sl_fmtlab()
1052
1053 ==DOC==*/
1054
1055 DLL_EXPORT
1056 int
sl_ds1(SLLABEL * lab,int type,char * dsn,char * volser,int volseq,int dsseq,char * expdt,int blocks)1057 sl_ds1( SLLABEL *lab,
1058 int type,
1059 char *dsn,
1060 char *volser,
1061 int volseq,
1062 int dsseq,
1063 char *expdt,
1064 int blocks )
1065 {
1066 int gdg;
1067 size_t len;
1068 size_t ndx;
1069 char wbuf[ 80 ];
1070
1071 /*
1072 || Initialize
1073 */
1074 memset( lab, ' ', sizeof( SLLABEL ) );
1075
1076 /*
1077 || Label ID
1078 */
1079 if( ( type != SLT_HDR ) && ( type != SLT_EOF ) && ( type != SLT_EOV ) )
1080 {
1081 return( SLE_INVALIDTYPE );
1082 }
1083 memcpy( lab->id, sl_alabs[ type ], 3 );
1084
1085 /*
1086 || Label number
1087 */
1088 lab->num[ 0 ] = '1';
1089
1090 /*
1091 || Special IEHINITT dataset name?
1092 */
1093 if( ( type == SLT_HDR ) && ( strcmp( dsn, SL_INITDSN ) == 0 ) )
1094 {
1095 memset( &lab->slds1, '0', sizeof( lab->slds1 ) );
1096 sl_atoe( NULL, lab, sizeof( SLLABEL ) );
1097 return( 0 );
1098 }
1099
1100 /*
1101 || Dataset ID
1102 */
1103 ndx = 0;
1104 len = strlen( dsn );
1105 if( len > 17 )
1106 {
1107 ndx = len - 17;
1108 len = 17;
1109 }
1110 memcpy( lab->slds1.dsid, &dsn[ ndx ], len );
1111
1112 /*
1113 || GDG generation and version
1114 */
1115 if( len > 9 )
1116 {
1117 gdg = 0;
1118 gdg += ( dsn[ len - 9 ] == '.' );
1119 gdg += ( dsn[ len - 8 ] == 'G' );
1120 gdg += ( isdigit( dsn[ len - 7 ] ) != 0 );
1121 gdg += ( isdigit( dsn[ len - 6 ] ) != 0 );
1122 gdg += ( isdigit( dsn[ len - 5 ] ) != 0 );
1123 gdg += ( isdigit( dsn[ len - 4 ] ) != 0 );
1124 gdg += ( dsn[ len - 3 ] == 'V' );
1125 gdg += ( isdigit( dsn[ len - 2 ] ) != 0 );
1126 gdg += ( isdigit( dsn[ len - 1 ] ) != 0 );
1127
1128 if( gdg == 9 )
1129 {
1130 memcpy( lab->slds1.genno, &dsn[ len - 7 ], 4 );
1131 memcpy( lab->slds1.verno, &dsn[ len - 2 ], 2 );
1132 }
1133 }
1134
1135 /*
1136 || Volser
1137 */
1138 len = strlen( volser );
1139 if( len > 6 )
1140 {
1141 return( SLE_VOLSER );
1142 }
1143 memcpy( lab->slds1.volser, volser, len );
1144
1145 /*
1146 || Volume sequence
1147 */
1148 if( volseq > 9999 )
1149 {
1150 return( SLE_VOLSEQ );
1151 }
1152 sprintf( wbuf, "%04u", volseq );
1153 memcpy( lab->slds1.volseq, wbuf, 4 );
1154
1155 /*
1156 || Dataset sequence
1157 */
1158 if( dsseq > 9999 )
1159 {
1160 return( SLE_DSSEQ );
1161 }
1162 sprintf( wbuf, "%04u", dsseq );
1163 memcpy( lab->slds1.dsseq, wbuf, 4 );
1164
1165 /*
1166 || Creation Date
1167 */
1168 sl_fmtdate( lab->slds1.crtdt, NULL, FALSE );
1169
1170 /*
1171 || Expiration Date
1172 */
1173 if( sl_fmtdate( lab->slds1.expdt, expdt, FALSE ) == NULL )
1174 {
1175 return( SLE_EXPDT );
1176 }
1177
1178 /*
1179 || Dataset security
1180 */
1181 memset( lab->slds1.dssec, '0', 1 );
1182
1183 /*
1184 || Block count - low
1185 */
1186 if( type == SLT_HDR )
1187 {
1188 blocks = 0;
1189 }
1190 sprintf( wbuf, "%010u", blocks );
1191 memcpy( lab->slds1.blklo, &wbuf[ 4 ], 6 );
1192
1193 /*
1194 || System code
1195 */
1196 memcpy( lab->slds1.syscd, "IBM OS/VS 370", 13 );
1197
1198 /*
1199 || Block count - high
1200 */
1201 sprintf( wbuf, "%10u", blocks );
1202 memcpy( lab->slds1.blkhi, wbuf, 4 );
1203
1204 /*
1205 || Convert to EBCDIC
1206 */
1207 sl_atoe( NULL, lab, sizeof( SLLABEL ) );
1208
1209 return 0;
1210 }
1211
1212 /*==DOC==
1213
1214 NAME
1215 sl_ds2 - Generate a data set label 2
1216
1217 SYNOPSIS
1218 #include "sllib.h"
1219
1220 int sl_ds2( SLLABEL *lab,
1221 int type,
1222 char *recfm,
1223 int lrecl,
1224 int blksize,
1225 char *jobname,
1226 char *stepname,
1227 char *trtch )
1228
1229 DESCRIPTION
1230 This function builds a data set label 2 based on the parameters
1231 provided and places it at the location pointed to by the "lab"
1232 parameter in EBCDIC.
1233
1234 The "type" parameter must be "SLT_HDR", "SLT_EOF", or "SLT_EOV".
1235
1236 The remaining parameters correspond to fields within the label
1237 and are converted to EBCDIC before storing.
1238
1239 The "recfm" parameter may be one of the following:
1240 F FS V VS U
1241 FA FSA VA VSA UA
1242 FM FSM VM VSM UM
1243 FB FBS VB VBS
1244 FBA FBSA VBA VBSA
1245 FBM FBSM VBM VBSM
1246
1247 The "trtch" parameter may be blank or one of the following:
1248 T C E ET P
1249
1250 RETURN VALUE
1251 The return value will be >= 0 if no errors are detected.
1252
1253 If an error is detected, then the return value will be < 0 and
1254 will be one of the following:
1255
1256 SLE_INVALIDTYPE Invalid label type specified
1257 SLE_RECFM Missing or invalid record format
1258 SLE_LRECL Invalid record length
1259 SLE_BLKSIZE Block size out of range
1260 SLE_JOBNAME Missing or invalid job name
1261 SLE_STEPNAME Missing or invalid step name
1262 SLE_TRTCH Invalid recording technique
1263
1264 NOTES
1265 This routine is normally accessed using the supplied "sl_hdr1",
1266 "sl_eof1", or "sl_eov1" macros.
1267
1268 Only the "most common" label fields have corresponding parameters
1269 so the user must supply any other desired values.
1270
1271 EXAMPLE
1272 //
1273 // Create a EOV2 label
1274 //
1275
1276 #include "sllib.h"
1277
1278 int main( int argc, char *argv[] )
1279 {
1280 SLFMT slfmt;
1281 SLLABEL sllab;
1282 int i;
1283
1284 sl_ds2( &sllab,
1285 SLT_EOF,
1286 "FB",
1287 80,
1288 32720,
1289 "HERCJOB",
1290 "HERCSTEP",
1291 "P" );
1292
1293 sl_fmtlab( &slfmt, &sllab );
1294
1295 for( i = 0 ; slfmt.key[ i ] != NULL ; i++ )
1296 {
1297 printf("%-20.20s: '%s'\n", slfmt.key[ i ] , slfmt.val[ i ] );
1298 }
1299
1300 return( 0 );
1301 }
1302
1303 SEE ALSO
1304 sl_fmtlab()
1305
1306 ==DOC==*/
1307
1308 DLL_EXPORT
1309 int
sl_ds2(SLLABEL * lab,int type,char * recfm,int lrecl,int blksize,char * jobname,char * stepname,char * trtch)1310 sl_ds2( SLLABEL *lab,
1311 int type,
1312 char *recfm,
1313 int lrecl,
1314 int blksize,
1315 char *jobname,
1316 char *stepname,
1317 char *trtch )
1318 {
1319 int i;
1320 size_t len;
1321 char wbuf[ 80 ];
1322
1323 /*
1324 || Initialize
1325 */
1326 memset( lab, ' ', sizeof( SLLABEL ) );
1327
1328 /*
1329 || Label ID
1330 */
1331 if( ( type != SLT_HDR ) && ( type != SLT_EOF ) && ( type != SLT_EOV ) )
1332 {
1333 return( SLE_INVALIDTYPE );
1334 }
1335 memcpy( lab->id, sl_alabs[ type ], 3 );
1336
1337 /*
1338 || Label number
1339 */
1340 lab->num[ 0 ] = '1';
1341
1342 /*
1343 || Record format/Block Attribute/Control
1344 */
1345 if( recfm == NULL )
1346 {
1347 return( SLE_RECFM );
1348 }
1349
1350 for( i = 0 ; i < (int)VALFMCNT ; i++ )
1351 {
1352 if( strcmp( recfm, valfm[ i ].recfm ) == 0 )
1353 {
1354 break;
1355 }
1356 }
1357
1358 if( i == VALFMCNT )
1359 {
1360 return( SLE_RECFM );
1361 }
1362
1363 lab->slds2.recfm[ 0 ] = valfm[ i ].f;
1364 lab->slds2.blkattr[ 0 ] = valfm[ i ].b;
1365 lab->slds2.ctrl[ 0 ] = valfm[ i ].c;
1366
1367 /*
1368 || Block size
1369 */
1370 if( blksize == 0 )
1371 {
1372 return( SLE_BLKSIZE );
1373 }
1374
1375 if( blksize > 32760 )
1376 {
1377 sprintf( wbuf, "%10u", blksize );
1378 memcpy( lab->slds2.lblkln, wbuf, 10 );
1379 memcpy( lab->slds2.blksize, "00000", 5 );
1380 }
1381 else
1382 {
1383 sprintf( wbuf, "%05u", blksize );
1384 memcpy( lab->slds2.blksize, wbuf, 5 );
1385 }
1386
1387 /*
1388 || Logical record length
1389 */
1390 switch( lab->slds2.recfm[ 0 ] )
1391 {
1392 case 'F':
1393 if( ( valfm[ i ].b == 'S' ) || ( valfm[ i ].b == ' ' ) )
1394 {
1395 if( lrecl != blksize )
1396 {
1397 return( SLE_LRECL );
1398 }
1399 }
1400 else
1401 {
1402 if( ( blksize % lrecl ) != 0 )
1403 {
1404 return( SLE_LRECL );
1405 }
1406 }
1407 break;
1408
1409 case 'V':
1410 if( valfm[ i ].b == ' ' )
1411 {
1412 if( ( lrecl + 4 ) != blksize )
1413 {
1414 return( SLE_LRECL );
1415 }
1416 }
1417 else if( valfm[ i ].b == 'B' )
1418 {
1419 if( ( lrecl + 4 ) > blksize )
1420 {
1421 return( SLE_LRECL );
1422 }
1423 }
1424 break;
1425
1426 case 'U':
1427 if( lrecl != 0 )
1428 {
1429 return( SLE_LRECL );
1430 }
1431 break;
1432 }
1433 sprintf( wbuf, "%05u", lrecl );
1434 memcpy( lab->slds2.lrecl, wbuf, 5 );
1435
1436 /*
1437 || Jobname and stepname
1438 */
1439 if( jobname != NULL )
1440 {
1441 if( stepname == NULL )
1442 {
1443 return( SLE_STEPNAME );
1444 }
1445
1446 len = strlen( jobname );
1447 if( len > 8 )
1448 {
1449 return( SLE_JOBNAME );
1450 }
1451
1452 len = strlen( stepname );
1453 if( len > 8 )
1454 {
1455 return( SLE_STEPNAME );
1456 }
1457 }
1458 else
1459 {
1460 if( stepname != NULL )
1461 {
1462 return( SLE_JOBNAME );
1463 }
1464 }
1465 sprintf( wbuf, "%-8.8s/%-8.8s", jobname, stepname );
1466 memcpy( lab->slds2.jobid, wbuf, 17 );
1467
1468 /*
1469 || Density
1470 */
1471 lab->slds2.den[ 0 ] = '0';
1472
1473 /*
1474 || Dataset position
1475 */
1476 lab->slds2.dspos[ 0 ] = '0';
1477
1478 /*
1479 || Tape recording technique
1480 */
1481 if( trtch != NULL )
1482 {
1483 len = strlen( trtch );
1484 if( len < 1 || len > 2 )
1485 {
1486 return( SLE_TRTCH );
1487 }
1488
1489 switch( trtch[ 0 ] )
1490 {
1491 case 'T': case 'C': case 'P': case ' ':
1492 lab->slds2.trtch[ 0 ] = trtch[ 0 ];
1493 break;
1494
1495 case 'E':
1496 lab->slds2.trtch[ 0 ] = trtch[ 0 ];
1497 if( len == 2 )
1498 {
1499 if( trtch[ 1 ] != 'T' )
1500 {
1501 return( SLE_TRTCH );
1502 }
1503 lab->slds2.trtch[ 1 ] = trtch[ 1 ];
1504 }
1505 break;
1506
1507 default:
1508 return( SLE_TRTCH );
1509 break;
1510 }
1511 }
1512
1513 /*
1514 || Device serial number
1515 */
1516 sprintf( wbuf, "%06u", rand() );
1517 memcpy( lab->slds2.devser, wbuf, 6 );
1518
1519 /*
1520 || Checkpoint dataset identifier
1521 */
1522 lab->slds2.ckptid[ 0 ] = ' ';
1523
1524 /*
1525 || Convert to EBCDIC
1526 */
1527 sl_atoe( NULL, lab, sizeof( SLLABEL ) );
1528
1529 return 0;
1530 }
1531
1532 /*==DOC==
1533
1534 NAME
1535 sl_usr - Generate a user label
1536
1537 SYNOPSIS
1538 #include "sllib.h"
1539
1540 int sl_usr( SLLABEL *lab,
1541 int type,
1542 int num,
1543 char *data )
1544
1545 DESCRIPTION
1546 This function builds a user label based on the parameters provided
1547 and places it at the location pointed to by the "lab" parameter in
1548 EBCDIC.
1549
1550 The "type" parameter must be "SLT_UHL" or "SLT_UTL" and the "num"
1551 parameter must be 1 through 8.
1552
1553 The remaining parameter corresponds to fields within the label
1554 and is converted to EBCDIC before storing.
1555
1556 RETURN VALUE
1557 The return value will be >= 0 if no errors are detected.
1558
1559 If an error is detected, then the return value will be < 0 and
1560 will be one of the following:
1561
1562 SLE_DATA Missing or invalid user data
1563
1564 NOTES
1565 This routine is normally accessed using the supplied "sl_uhl*"
1566 or "sl_utl*" macros.
1567
1568 EXAMPLE
1569 //
1570 // Create a UHL6 label
1571 //
1572
1573 #include "sllib.h"
1574
1575 int main( int argc, char *argv[] )
1576 {
1577 SLFMT slfmt;
1578 SLLABEL sllab;
1579 int i;
1580
1581 sl_usr( &sllab,
1582 SLT_EOF,
1583 6,
1584 "Hercules Emulated Tape" );
1585
1586 sl_fmtlab( &slfmt, &sllab );
1587
1588 for( i = 0 ; slfmt.key[ i ] != NULL ; i++ )
1589 {
1590 printf("%-20.20s: '%s'\n", slfmt.key[ i ] , slfmt.val[ i ] );
1591 }
1592
1593 return( 0 );
1594 }
1595
1596 SEE ALSO
1597 sl_fmtlab()
1598
1599 ==DOC==*/
1600
1601 DLL_EXPORT
1602 int
sl_usr(SLLABEL * lab,int type,int num,char * data)1603 sl_usr( SLLABEL *lab,
1604 int type,
1605 int num,
1606 char *data )
1607 {
1608 size_t len;
1609
1610 /*
1611 || Initialize
1612 */
1613 memset( lab, ' ', sizeof( SLLABEL ) );
1614
1615 /*
1616 || Label ID
1617 */
1618 if( ( type != SLT_UHL ) && ( type != SLT_UTL ) )
1619 {
1620 return( SLE_INVALIDTYPE );
1621 }
1622 memcpy( lab->id, sl_elabs[ type ], 3 );
1623
1624 /*
1625 || Label number
1626 */
1627 if( ( num < 1 ) || ( num > 8 ) )
1628 {
1629 return( SLE_INVALIDNUM );
1630 }
1631 lab->num[ 0 ] = '0' + num;
1632
1633 /*
1634 || User data
1635 */
1636 if( data == NULL )
1637 {
1638 return( SLE_DATA );
1639 }
1640
1641 len = strlen( data );
1642 if( len == 0 || len > 76 )
1643 {
1644 return( SLE_DATA );
1645 }
1646 memcpy( lab->slusr.data, data, len );
1647
1648 /*
1649 || Convert to EBCDIC
1650 */
1651 sl_atoe( NULL, lab, sizeof( SLLABEL ) );
1652
1653 return 0;
1654 }
1655
1656 /*==DOC==
1657
1658 NAME
1659 sl_error - Returns a text message for an SL error code
1660
1661 SYNOPSIS
1662 #include "sllib.h"
1663
1664 char *sl_error( int rc )
1665
1666 DESCRIPTION
1667 Simply returns a pointer to a string that describes the error
1668 code passed in the "rc" parameter.
1669
1670 RETURN VALUE
1671 The return value is always valid and no errors are returned.
1672
1673 EXAMPLE
1674 //
1675 // Print text for SLE_DSSEQ.
1676 //
1677
1678 #include "sllib.h"
1679
1680 int main( int argc, char *argv[] )
1681 {
1682 printf( "SLLIB error: %d = %s\n",
1683 SLE_DSSEQ,
1684 sl_error( SLE_DSSEQ ) );
1685
1686 return( 0 );
1687 }
1688
1689 SEE ALSO
1690
1691 ==DOC==*/
1692
1693 DLL_EXPORT
1694 const char *
sl_error(int rc)1695 sl_error( int rc )
1696 {
1697 /*
1698 || If not an error just return the "OK" string
1699 */
1700 if( rc >= 0 )
1701 {
1702 rc = 0;
1703 }
1704
1705 /*
1706 || Turn it into an index
1707 */
1708 rc = -rc;
1709
1710 /*
1711 || Within range?
1712 */
1713 if( rc >= (int)SL_ERRSTR_MAX )
1714 {
1715 rc = SL_ERRSTR_MAX - 1;
1716 }
1717
1718 /*
1719 || Return string
1720 */
1721 return( sl_errstr[ rc ] );
1722 }
1723