home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: Graphics / Graphics.zip / blendgif.zip / parsegif.rxx < prev    next >
Text File  |  1999-01-26  |  37KB  |  1,312 lines

  1. /************************************************************
  2.                             PARSEGIF
  3.             Procedures to extract information from a  gif file.
  4.  
  5. Notes: 
  6.  * In the descriptions below:
  7.    > ABLOCK is an actual string of bytes; as pulled from gif file,
  8.        or suitable for writing to a gif file.
  9.    > CT_NAME is a string containing the name of the "matrix of 
  10.        color table values" stem variable.
  11.        You MUST set it's value before calling procedures that
  12.        use it.  For example:   ct_name='MY_CT.'
  13.        (note that you MUST include the . at the end of the stem name)
  14.    > STUFF is a space or comma delimited list of variables returned
  15.        by one of these procedures.
  16.    > IMG_NAME is a string containing the name of a "matrix of pixels"
  17.        stem avariable.
  18.        You MUST set it's value before calling procedures that
  19.        use it.  For example:   imgt_name='IMG_NAME.'
  20.        (note that you MUST include the . at the end of the stem name)
  21.  
  22.  *  Use read_gif_block to read  various "blocks" from a GIF file, 
  23.     these blocks may then be used as input to the other 
  24.     For example: 
  25.                  ablock=read_gif_block(a_gif_file,1,'LSD')
  26.                  ablock=read_gif_block(a_gif_file,3,"IMG")
  27.                  ablock=read_gif_block(gifstring,imgnum,'GCE',1)
  28.     
  29.  * Several of these procedures work with color tables. Color tables
  30.    are stored in stem variables, which have the structure:
  31.         ct.0 = # of colors
  32.         ct.!r.n = red value for color n
  33.         ct.!g.n = green value for color n
  34.         ct.!b.n = blue value for color n
  35.     where n =0 ... (ctable.0-1), and ct is the "color table name".
  36.    
  37.     Prior to calling a color table using/returning procedure,
  38.     the "color table name" must be defined. 
  39.     To do this, just set:
  40.          CT_NAME='a_color_table_name.'
  41.     For example:
  42.          CT_NAME='MY_CT.'
  43.          MY_CT.=0
  44.     Note that you MUST include the . after the actual name. Use of MY_CT.=0
  45.     (to set the default value of the MY_CT. "tail" values) is strictly optional.
  46.     
  47.     Example:
  48.          CT_NAME='IMG3_CT.'
  49.          IMG_NAME='IMG_PIX.'
  50.          ablock=read_gif_block(gif_file,3,'IMG')
  51.          stuff=READ_IMAGE_BLOCK(ablock,0)
  52.          (the IMG3_CT. stem variable will contain the local color table
  53.           for the 3rd image of gif_file, assuming one exists).
  54.  
  55.  *  Several of these procedures work with a matrix of pixel values.
  56.     As with color tables, these are stored in stem variables, which
  57.     requires one to assign a value to the IMG_NAME variable. For
  58.     example:
  59.           IMG_NAME='img1.'
  60.           img1.=0
  61.     Note that you MUST include the . after the actual name. 
  62.  
  63.    The structure of this stem variable is (assuming a stem name of img1):
  64.       img1.!rows = # rows
  65.       img1.!cols = # cols
  66.    and each row of the image is in:
  67.       img1.0
  68.         ...
  69.       img1.nrr
  70.    where:
  71.       nrr=# rows-1  
  72.       and each "row" is a string of length img1.!cols.  
  73.           Each character in this string corresponds (is the d2c) for
  74.           a pixel value.  
  75.     Thus, to get the pixel value of the 5 column of the 10th row:
  76.                 avalue=c2d(substr(img1.10,5,1)) 
  77.  
  78. List of Procedures:
  79.  ablock=READ_GIF_BLOCK(giffile,imgnum,infotype,is_string)
  80.  ablock=MAKE_ANIMATION_BLOCK(iter) 
  81.   niter=READ_ANIMATION_BLOCK(ablock)
  82.  ablock=MAKE_COMMENT_BLOCK(a_comment)
  83.   stuff=READ_COMMENT_BLOCK(ablock)
  84.  ablock=MAKE_GCE_BLOCK(tcflag,tcindex,delay,disposal,useinlag)
  85.   stuff=READ_GCE_BLOCK(ablock)
  86.  ablock=MAKE_IMAGE_BLOCK(lpos,tpos,wid,hei,lct,lctsize,inter,sort,imgdata)
  87.   stuff=READ_IMAGE_BLOCK(ablock,to_matrix)
  88.  ablock=MAKE_LSD_BLOCK(width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize)
  89.   stuff=READ_LSD_BLOCK(ablock)
  90.  ablock=MAKE_PTE_BLOCK(tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess)
  91.   stuff=READ_PTE_BLOCK(ablock)
  92.  ablock=MAKE_TERMINATOR_BLOCK()
  93.  
  94.  
  95. Description of procedures:
  96.  
  97. ablock=read_gif_block(giffile,imgnum,infotype,is_string)
  98.     Pull a "block" from a gif file.
  99.  
  100.    Where:
  101.         giffile : A file name OR a string containing the contents of a gif file
  102.         nth  : Get block associated with this image, comment, or app block.
  103.         infotype : Type of block to get
  104.         is_string: If 1, then GIFFILE argument is a string, otherwise it's
  105.                    a file name (which read_gif_block will read)
  106.    Values of infotype:
  107.         IMG  -- get the nth "image descriptor" of the imgnum image.
  108.                 To examine: use READ_IMG_BLOCK
  109.         CMT  -- get the nth "comment extension". 
  110.                 To examine: use READ_COMMENT_BLOCK
  111.         ACE  -- get the "application control extension" for the nth application.
  112.                 To examine: use READ_ANIMATION_BLOCK  -- but this is only
  113.                 useful if it's an "animation" block.
  114.         LSD  -- get the "logical control descriptor", including the "GIF89a"
  115.                 (or "GIF87a") header (nth is ignored -- there is only one
  116.                 LSD per file). Note that the LSD is REQUIRED -- all gif files
  117.                 must have start with an LSD. 
  118.                 To examine: use  READ_LSD_BLOCK.
  119.         GCE  -- get the nth "graphic control extension". 
  120.                 To examine: use READ_GCE_BLOCK.
  121.         PTE --  get the nth "plain text extension".
  122.         LST  -- return a spaced delimited list of INFOTYPE codes.
  123.  
  124.   Note that LST is different -- it returns a string. 
  125.   Several additional codes may appear in this "LST" of blocks.
  126.        00 = a '00'x block (a harmless error)
  127.       TRM = terminator -- should ALWAYS be the last code in LST
  128.  
  129.   Note: if an error occurs, ablock will be a string starting with "ERROR",
  130.         and followed by a short error message.
  131.  
  132. ablock=MAKE_ANIMATION_BLOCK(iter) 
  133.       Create an "animation" applications block.
  134.  
  135.       Where:
  136.           iter= # of iterations
  137.  
  138.  
  139. stuff=READ_ANIMATION_BLOCK(ablock(
  140.      Extract # iterations from a "netscape" animation applications 
  141.      control extension (ACE) block.
  142.  
  143.      You can parse stuff with:
  144.         parse var stuff appname','niters
  145.      Where
  146.         appname = name of applicaton block
  147.         niters  = if "NETSCAPE" is the appname, then this is the # of iterations
  148.                   Otherwise, niters=''                                                        
  149.  
  150. ablock=MAKE_COMMENT_BLOCK(a_comment)
  151.    Make a comment block.
  152.  
  153.    Where:
  154.         a_comment = A string containing your comment. Can be any length,
  155.                     and contain CRLFs.
  156.  
  157. stuff=READ_COMMENT_BLOCK(ablock)
  158.    Extract comment from a comment block.
  159.  
  160.    The comment is the only item returned in stuff.
  161.  
  162. ablock=MAKE_GCE_BLOCK(tcflag,tcindex,delay,disposal,useinlag)
  163.    Make a "graphics control extension" block
  164.  
  165.    Where:
  166.         tcflag  = transparent color index flag. If not 1, transparent
  167.                   color still written (Tcindex), but will be ignored by
  168.                   image dipslay programs.
  169.         tcindex = index of the transparent color.
  170.         delay = Delay time (1/100 ths seconds) -- wait this time AFTER
  171.                 displaying image
  172.         dispoal = Disposal method (after delay is over, or userinput taken)
  173.                     0=no action, 1=retain image
  174.                     2=set to background  3=restore to previous
  175.         useinflag = User input flag (1=yes)
  176.  
  177. stuff=READ_GCE_BLOCK(ablock)
  178.   Obtain information from a graphics control extension block.
  179.  
  180.   To get the actual variables, use:
  181.      parse var stuff  disposal usrinflag tcflag delay tcindex
  182.  
  183.   Where the variables are as defined in MAKE_GCE_BLOCK.
  184.  
  185. ablock=MAKE_IMAGE_BLOCK(lpos,tpos,wid,hei,lct,lctsize,inter,sort,imgdata)
  186.     Create an "image descriptor" box.
  187.  
  188.     Where:
  189.         lpos = column number of the left edge of the image (wrt to
  190.                logical screen)
  191.         tpos = row number of the right edge of the image 
  192.         wid= image width in pixels
  193.         hei= image height in pixels
  194.         lct = local color table flag -- set to 1 if a color table 
  195.               to create a local color table
  196.               If LCT=1, then you must "setup the ct_name color table"
  197.               before calling MAKE_IMAGE_BLOCK
  198.       lctsize= size of local color table. if no specified, ct_name.0 is used.
  199.                 If LCT=0, lctsize will still be written (even though
  200.                 no color table is written). This is sort of pointless,
  201.                 but does seem to be a sop.
  202.          inter = interlace flag 
  203.           sort = if 1, indicates that the color table is sorted, with most
  204.                  used color at top. 
  205.       imgdata= If specifed, this should contain:
  206.                    the actual lzw-compressed image data, (including the 
  207.                    "lzw" starting byte)
  208.                If not specified, or if equal to 0, then
  209.                     MAKE_IMAGE_BLOCK will use the contents of the stem variable
  210.                     declared by the IMG_NAME variable (see description above)
  211.                     
  212.     Note: when using a stem variable as the contents of the gif
  213.           image (when imgdata=0), the !cols and !rows "tails" will
  214.           NOT be used -- instead, the width and height variables (specified
  215.           in the argument list) are used. 
  216.           Of course, one would typically make sure that these were equal...
  217.  
  218. stuff=READ_IMAGE_BLOCK(ablock,to_matrix)
  219.     Pull information out of an "image descriptor" block
  220.  
  221.     Where:
  222.        ablock =an image descriptor block; say as retrieved with read_gif_block
  223.        tempfile =  If missing or 0, then
  224.                      ignore
  225.                    If 1, then  
  226.                       write the pixel values of the image to "IMG_NAME"
  227.                       stem variable (see the introductory notes for details).
  228.                       A temporary file, with a name like $TMPnnnn.TMP, 
  229.                       will be temporarily created.
  230.                    If a file name, then
  231.                       Same as 1, but use this filename (instead of a
  232.                       $TMPnnnn.TMP file name) for the temporary file.
  233.               
  234.     The actual information is then obtained by using:
  235.          parse var stuff lpos tpos width hei lct interl sort ',' imgdata
  236.         (see MAKE_IMAGE_BLOCK for a description of these variables).
  237.      and (if to_matrix is appropriately specified)
  238.         by examining the stem variable named by IMG_NAME.
  239.  
  240.     Notes:
  241.          * be SURE to include a ',' before the imgdata (in the parse)
  242.          * if there is any chance the image block includes a local color
  243.            table, be sure to set the value of the CT_NAME variable
  244.            before calling READ_IMAGE_BLOCK
  245.          * if you specify to_matrix, be sure to set the value of the
  246.            IMG_NAME variable before calling READ_IMAGE_BLOCK.
  247.  
  248.  
  249. ablock=MAKE_LSD_BLOCK(width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize)
  250.       Make a logical screen descriptor  block -- including the "GIF89a"
  251.       header (the first 6 six characters in a gif file).
  252.  
  253.       Where:
  254.           width = "logical screen" width (in pixels)
  255.           height= "logical screen" height (in pixels)
  256.           gcflag= set to 1 if a global color table is to be created.
  257.                  If GCFLAG=1, then you must "setup the ct_name color table"
  258.                  beforecalling MAKE_LSD_BLOCK
  259.           colres=2**(colres+1)= color resolution of image creater(rarely used)
  260.           sort = if 1, indicates that the color table is sorted, with most
  261.                  used color at top. 
  262.           bkgcolor = background color index (rarely used)
  263.           aspect = height to width aspect (rarely used)
  264.           gcsize= size of color table. if no specified, ct_name.0 is used.
  265.                  
  266.  
  267. stuff=READ_LSD_BLOCK(ablock)
  268.   Pull information from an logical screen descriptor block
  269.  
  270.     Ablock is an logical screen descriptor block; say as 
  271.     retrieved with read_gif_block.
  272.      
  273.     The actual information is then obtained by using:
  274.         parse var st width height gcflag colres sort bkgcolor aspect
  275.  
  276.      Where the variables are as defined in MAKE_LSD_BLOCK
  277.  
  278. ablock=MAKE_PTE_BLOCK(tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess)
  279.    Create a "plain text" extensions block
  280.  
  281.    Where:
  282.         tgleft = pixel column number of left of text grid
  283.         tgtop  = pixel row number of top of text grid
  284.        tgwidth = width of text grid in pixels
  285.       tgheight = height of text grid in pixels
  286.       ccwidth  = width of each cell in pixels
  287.       ccheight = height of each cell in pixels
  288.       tfore    = text foreground color table index (into global color table)
  289.       tback    = text background color table index (into global color table)
  290.        amess   = message string
  291.  
  292. stuff=READ_PTE_BLOCK(ablock)
  293.    Pull information from a plain text extension block.
  294.  
  295.    The actual information can be obtained using:
  296.       parse stuff  tgleft tgtop tgwidth tgheight ccwidth ccheight tfore tback ',' ptext
  297.    Where the variables are as defined in MAKE_PTE_BLOCK
  298.         
  299.  
  300. ablock=MAKE_TERMINATOR_BLOCK()
  301.    Create a "terminator" block.
  302.    No arguments are required (this is simple a constant equal to '3b'x.
  303.  
  304.   
  305. **********************************************************************/
  306.  
  307.  
  308.  
  309. /*******************/
  310. /* make an image block (note use of img_name and ct_name )
  311. Example: 
  312.   ct_name='ct1.' ; img_name='img1.'
  313.   stuff2=make_image_block(lpos,tpos,wid,hei,lct,lcsize,inter,sort,imgdata)
  314. */
  315.  
  316.  
  317. make_image_block:procedure expose (ct_name) (img_name)
  318.  
  319. parse arg lpos,tpos,width,height,lctflag,lcsize,interlace,sortflag,imgdata
  320.  
  321. astuff='2c'x
  322.  
  323. astuff=astuff||dd2c(lpos,2)
  324. astuff=astuff||dd2c(tpos,2)
  325. astuff=astuff||dd2c(width,2)
  326. astuff=astuff||dd2c(height,2)
  327.  
  328. /* create a byte containg several flags */
  329.  
  330. if interlace<>1 then interlace=0
  331. if sortflag<>1 then sortflag=0
  332. if lctflag<>1 then lctflag=0
  333.  
  334. ct0=value(ct_name'0')
  335. if lcsize='' | datatype(lcsize)<>'NUM' then
  336.    isizect=ct0
  337. else 
  338.    isizect=lcsize
  339.  
  340. select          /* 3 bit rep of 2**(sizect+1), rounded up */
  341.    when isizect>128 then do 
  342.          sizect='111' ; is2=256 ;end
  343.    when isizect>64  then do
  344.          sizect='110' ; is2=128 ; end 
  345.    when isizect>32  then do 
  346.         sizect='101' ; is2=64 ; end
  347.    when isizect>16  then do 
  348.         sizect='100' ; is2=32 ;end 
  349.    when isizect>8   then do 
  350.         sizect='011' ; is2=16 ; end ;
  351.    when isizect>4   then do
  352.          sizect='010' ; is2=8 ; end
  353.    when isizect>2   then do 
  354.         sizect='001' ; is2=4 ; end
  355.    otherwise do
  356.         sizect='000' ; is2=2 ; end
  357. end
  358.  
  359. lc=lctflag||interlace||sortflag||'00'||sizect
  360. aa=x2c(b2x(lc))
  361.  
  362. astuff=astuff||aa
  363.  
  364. /* add color table info */
  365. if lctflag=1 then do
  366.   lsd=''
  367.   do mm=0 to min(isizect,ct0)-1
  368.      ii=value(ct_name'!r.'mm)
  369.      lsd=lsd||d2c(ii)
  370.      ii=value(ct_name'!g.'mm)
  371.      lsd=lsd||d2c(ii)
  372.      ii=value(ct_name'!b.'mm)
  373.      lsd=lsd||d2c(ii)
  374.    end /* do */
  375.    if isizect<is2 then do   /* pack the color table */
  376.      do isizect+1 to is2
  377.        lsd=lsd||'000000'x
  378.     end /* do */
  379.   end
  380.   astuff=astuff||lsd
  381. end
  382.  
  383. if imgdata<>'' & imgdata<>'0' then do
  384.   astuff=astuff||imgdata
  385.   return astuff
  386. end
  387.  
  388. /* else, create lzw comppressed image from img_name stem */
  389.  
  390. tempname=imgdata
  391.  
  392. if tempname=1 then do
  393.    usename=systempfilename('$TM1????.TMP')
  394. end
  395. else do
  396.    if pos('?',tempname)>0 then
  397.       usename=systempfilename(tempname)
  398.    else
  399.       usename=TEMPNAME
  400. end
  401.  
  402. ncols=width
  403. nrows=height
  404. messim=rxgdimagecreate(ncols,nrows)
  405. if messim<2 then do
  406.   say "Error Could not create temporary gif image "
  407.   return ''
  408. end
  409.  
  410. pxs.=0
  411. do mr=0 to nrows-1              /* FROM STEM ARRAY TO IMAGE */
  412.    alin=value(img_name||mr)
  413.    do mc=0 to ncols-1
  414.      PXS.MC=c2d(substr(alin,mc+1,1))
  415.    end /* do */
  416.    styled=RxgdImageSetStyle(messim, pxs, ncols)  
  417.    rc=RxgdImageLine(messim, 0,mr,ncols-1,mr,styled)
  418. end
  419.  
  420. DO III=0 TO 255
  421.    FOO=RXGDIMAGECOLORALLOCATE(MESSIM,III,255-III,0)
  422. end /* do */
  423. foo=rxgdimageinterlace(messim,interlace)
  424. foo=rxgdimagegif(messim,usename)
  425. foo=rxgdimagedestroy(messim)
  426.  
  427. oof=charin(usename,1,chars(usename))
  428. if oof="" then  do
  429.  say "Error retrieving temporary gif file"
  430.  return ""
  431. end
  432. foo=stream(USENAME,'c','close')
  433. foo=sysfiledelete(usename)
  434.  
  435. OOF2=read_gif_block(OOF,1,'IMG',1)
  436.  
  437. ct_name='ctmp.'
  438. stuff2=read_image_block(oof2,0)
  439. parse var stuff2 . ',' imgdata
  440. return astuff||imgdata
  441.  
  442.  
  443. /*******************/
  444. /* read an image_block
  445.  
  446. Example:
  447.   ct_name="CT3."
  448.   ct3.=0 ; img_name='img1.'
  449.   ablock=read_gif_block(giffile,1,'IMG')
  450.   stuff=read_image_block(ablock,0)
  451.   parse var stuff leftpos toppos width height lctflag interlaceflag sortflag ','||imgdata
  452.   say " Left top at "leftpos toppos
  453.   say " Width height = " width height
  454.   say " Interlace:" interlaceflag 
  455.   say ' local ct = 'lctflag ' ( sorted = 'sortflag
  456.   if lctflag=1 then do
  457.      say " # colors in lct = " ct3.0 ct3.!r.1 ct3.!g.1 ct3.!b.1
  458.   end
  459.   say " Imgsize = " length(imgdata)
  460.  
  461. and if tomtx is specified (=1 , or equal to a filename), then also
  462. create the IMG_NAME stem variable "matrix of pixel values"
  463.  
  464. */
  465.  
  466. read_image_block:procedure expose (ct_name) (IMG_NAME)
  467.  
  468. parse arg ablock,tomtx
  469.  
  470. il=substr(ablock,2,2)
  471. lpos=c2d(reverse(il))
  472. it=substr(ablock,4,2)
  473. tpos=c2d(reverse(it))
  474. iw=substr(ablock,6,2)
  475. width=c2d(reverse(iw))
  476. ih=substr(ablock,8,2)
  477. height=c2d(reverse(ih))
  478.  
  479. pf=substr(ablock,10,1)
  480.  
  481. pf2= x2b(c2x(pf))
  482. lctflag=substr(pf2,1,1)
  483. interlace=substr(pf2,2,1)
  484. sortflag=substr(pf2,3,1)
  485.  
  486. lctsize=right(pf2,3)
  487. t=right(lctsize,8,0)
  488.  
  489. lctsize= x2d(b2x(t))
  490.  
  491. lctsize=2**(lctsize+1)
  492. imgat=11
  493.  
  494. if lctflag=1 then do
  495.    ith=0
  496.    do m0=1 to (lctsize*3) by 3
  497.       mm=m0+10
  498.       aa=value(ct_name'!r.'ith,c2d(substr(ablock,mm,1)))
  499.       aa=value(ct_name'!g.'ith,c2d(substr(ablock,mm+1,1)))
  500.       aa=value(ct_name'!b.'ith,c2d(substr(ablock,mm+2,1)))
  501.       ith=ith+1
  502.    end
  503.    imgat=mm+1
  504. end
  505.  
  506. aa=value(ct_name'0',lctsize)
  507.  
  508. daimage=substr(ablock,imgat)    /* get rest of stuff in image descriptor block */
  509.  
  510. /* note: color table in exposed stem */
  511. if tomtx="" | tomtx=0 then
  512.    return lpos tpos width height lctflag lctsize interlace sortflag ','||daimage
  513.  
  514. /* else, create the img_name stem var */
  515.  
  516. tempname=tomtx
  517.  
  518. if tempname=1 then do
  519.    usename=systempfilename('$TM2????.TMP')
  520. end
  521. else do
  522.    if pos('?',tempname)>0 then
  523.       usename=systempfilename(tempname)
  524.    else
  525.       usename=tempname
  526. end
  527.  
  528.  
  529. /* make the gif file in memory (very simple version) */
  530. aa=MAKE_LSD_BLOCK(width,height,0,7,0,0,,)
  531. aa=aa||ablock||make_terminator_block()
  532.  
  533. arf=charout(usename,aa,1)
  534. if arf<>0 then do  
  535.    say  "Error writing temporary gif file:" usename
  536.    return 0
  537. end
  538. foo=stream(usename,'c','close')
  539. /* now read with rxgd */
  540. dim= RxgdImageCreateFromGIF(usename)
  541. if dim<=1 then do
  542.   say " Error reading temporary gif file: " usename
  543.   oo=sysfiledelete(usename)
  544.   return 0
  545. end
  546.   
  547. nrows=RxgdImageSY(dim)
  548. ncols=rxgdimageSx(dim)
  549. foo=value(img_name'!ROWS',nrows)
  550. foo=value(img_name'!COLS',ncols)
  551.  
  552. ndid=0
  553. do ny=0 to nrows-1              /* FROM IMAGE TO STEM ARRAY */
  554.   foo=rxgdimagegetrowpixels(dim,ny,pxels)
  555.   alin=''
  556.   do nx=1 to ncols
  557.      alin=alin||d2c(pxels.nx)
  558.   end
  559.   foo=value(img_name||ny,alin)
  560. end
  561. foo=rxgdimagedestroy(dim)
  562. foo=stream(usename,'c','close')
  563. oo=sysfiledelete(usename)
  564.  
  565. return lpos tpos width height lctflag lctsize interlace sortflag ','||daimage
  566.  
  567. exit
  568.  
  569.  
  570.  
  571.  
  572.  
  573. /*******************/
  574. /* make a netscape app block, for animated images, with niter iterations */
  575.  
  576. Example:
  577.   niter=50
  578.   nu_anim_block=make_animation_block(niter) 
  579.  
  580. */
  581.  
  582. make_animation_block:procedure
  583. parse arg niter
  584. if niter="" then niter=0
  585. if niter<0 then niter=0
  586. if niter>65535 then niter=65334
  587.  
  588. ablock='21ff0b'x
  589. ablock=ablock||'NETSCAPE2.0'
  590. ablock=ablock||'03'x
  591. ablock=ablock||'01'x
  592. aiter=dd2c(niter,2)
  593. ablock=ablock||aiter
  594. ablock=ablock||'00'x
  595. return ablock
  596.  
  597. /*******************/
  598. /* read a netscape app block, for animated images, with niter iterations 
  599.  
  600. Example:
  601.  aa=read_animation_block(ablock)
  602.  
  603.  You can parse aa with:
  604.     parse var aa apname','niter
  605.   
  606.  If apname='NETSCAPE' then niter will be the iteration count.
  607.  Otherwise, niter will = ''
  608.  (that is, if not an animation block, niter='')
  609. */
  610.  
  611. read_animation_block:procedure
  612. parse arg ablock
  613.  
  614. apname=substr(ablock,4,8)
  615. apauth=substr(ablock,12,3)
  616. foo=apname||apauth
  617. if foo<>'NETSCAPE2.0' then return apname
  618. aiter=substr(ablock,17,2)
  619. niter=c2d(reverse(aiter))
  620. return apname','niter
  621.  
  622.  
  623.  
  624.  
  625. /*******************/
  626. /* create a graphics control extension block.
  627.  
  628. Example:
  629.   nu_gce_block=make_gce_block(tcflag,tcindex,delay,disposal,userinputflag)
  630.  
  631. */
  632.  
  633. make_gce_block:procedure
  634. parse arg tcflag,tcindex,delay,disposal,userinput
  635.  
  636. ablk='21f904'x
  637.  
  638. l3='000'
  639. if disposal='' then disposal=0
  640. ii= x2b(d2x(disposal))
  641. ii=right(ii,8,0)
  642. ii=right(ii,3)
  643. l3=l3||ii
  644.  
  645. if userinput=1 then
  646.   l3=l3||'1'
  647. else
  648.   l3=l3||'0'
  649.  
  650. if tcflag<>1 then
  651.    tcflag='0'
  652. else
  653.    tcflag='1'
  654. l3=l3||tcflag
  655.  
  656. l3a=x2c(b2x(l3))
  657.  
  658. ablk=ablk||l3a
  659.  
  660. if delay='' then delay=0
  661. delay=dd2c(delay,2)
  662.  
  663. if tcindex='' then tcindex=0
  664. tcindex=dd2c(tcindex,1)
  665. ablk=ablk||delay||tcindex||'00'x
  666.  
  667. return ablk
  668.  
  669.  
  670. /*******************/
  671. /* make logical screen descriptor 
  672. Example: (ct2. is a stem containing a color table )
  673.   ct_name='CT2.'
  674.   lsd_block=make_lsd_block(width,height,gcflag,colres,sort,bkgcolor,aspect)
  675.  
  676. */
  677.  
  678. make_lsd_block:procedure expose (ct_name)
  679. parse arg width,height,gcflag,colres,sort,bkgcolor,aspect,gcsize
  680.  
  681. /* organized as:
  682.  hd= 'GIFxxx' (1-6)
  683.  width = 2 bytes (7-8)
  684.  height=  2 bytes (9-10)
  685. packed = 1 byte (11) -- gcflag (1) colres (3) sort (1) sizect (3)
  686. bkgcolor =1 byte (12)
  687. aspect = 1 byte (13)
  688. colortable = 14 ... 13+ 2**(sizect+1)  bytes (rgbrgbrgb....)
  689. */
  690.  
  691. LSD='GIF89a'
  692.  
  693. A2=dD2C(WIDTH,2)
  694. A3=Dd2C(HEIGHT,2)
  695.  
  696. lsd=lsd||A2||A3
  697.  
  698. if gcflag=0 | gcflag='' then
  699.   l3='0'
  700. else
  701.   l3='1'
  702.  
  703. gcflag=l3
  704.  
  705. if colres='' then do
  706.   colres='111'
  707. end
  708. else do
  709.   colres=x2b(d2x(colres))
  710.   colres=right(colres,8,0)
  711.   colres=right(colres,3)
  712. end
  713.  
  714. l3=l3||colres
  715.  
  716. if sort='' | sort=0 then
  717.     l3=l3||'0'
  718. else
  719.     l3=l3||'1'
  720.  
  721.  
  722. ct0=value(ct_name'0')
  723. if gcsize='' | datatype(gcsize)<>'NUM' then
  724.   isizect=ct0
  725. else
  726.   isizect=gcsize
  727. select          /* 3 bit rep of 2**(sizect+1), rounded up */
  728.    when isizect>128 then do 
  729.          sizect='111' ; is2=256 ;end
  730.    when isizect>64  then do
  731.          sizect='110' ; is2=128 ; end 
  732.    when isizect>32  then do 
  733.         sizect='101' ; is2=64 ; end
  734.    when isizect>16  then do 
  735.         sizect='100' ; is2=32 ;end 
  736.    when isizect>8   then do 
  737.         sizect='011' ; is2=16 ; end ;
  738.    when isizect>4   then do
  739.          sizect='010' ; is2=8 ; end
  740.    when isizect>2   then do 
  741.         sizect='001' ; is2=4 ; end
  742.    otherwise do
  743.         sizect='000' ; is2=2 ; end
  744. end
  745. l3=l3||sizect
  746.  
  747. l3a=x2c(b2x(l3))
  748.  
  749. lsd=lsd||l3a
  750.  
  751. if bkgcolor='' then 
  752.    lsd=lsd||'00'x
  753. else
  754.    lsd=lsd||dd2c(bkgcolor,1)
  755.  
  756. if aspect='' then
  757.    lsd=lsd||d2c(0)
  758. else
  759.    lsd=lsd||dd2c(aspect,1)
  760.  
  761. /* add color table info */
  762. if gcflag=1 then do
  763.   do mm=0 to isizect-1
  764.     ii=value(ct_name'!r.'mm)
  765.     lsd=lsd||d2c(ii)
  766.     ii=value(ct_name'!g.'mm)
  767.     lsd=lsd||d2c(ii)
  768.     ii=value(ct_name'!b.'mm)
  769.     lsd=lsd||d2c(ii)
  770.   end /* do */
  771.   if isizect<is2 then do
  772.      do kkk=isizect+1 to is2
  773.         lsd=lsd||'000000'x
  774.      end /* do */
  775.   end
  776. end
  777.  
  778. return lsd
  779.  
  780. /*******************/
  781. /* make a comment block
  782. Example:
  783.   cmt="this is my comment on "||date()
  784.   nu_cmt_block=make_comment_block(cmt)
  785. */
  786.  
  787. make_comment_block:procedure
  788. parse arg acomment
  789. aa='21fe'x
  790. aa=aa||chunkit(acomment)
  791. return aa
  792.  
  793.  
  794. /*********/
  795. read_comment_block:procedure
  796. parse arg ain
  797.  iat=2
  798.  lena=length(ain)
  799.  amess=''
  800.  do forever       
  801.     if iat>lena then return ""   /* no block terminator -- error */
  802.     iat=iat+1      /* size of block */
  803.     ii=substr(ain,iat,1) ; ii=c2d(ii)
  804.     if ii=0 then return amess 
  805.     iat=iat+1
  806.     amess=amess||substr(ain,iat,ii)
  807.     iat=iat+ii-1
  808.  end /* do */
  809.  
  810. /*******************/
  811. /* plain text stuff */
  812. read_pte_block;procedure
  813. parse arg ain
  814.  
  815.   l1=substr(ain,1,2)
  816. tgleft=c2d(reverse(l1))
  817.   l2=substr(ain,3,2)
  818. tgtop=c2d(reverse(l2))
  819.  
  820.    l1=substr(ain,5,2)
  821. tgwidth=c2d(reverse(l1))
  822.    l2=substr(ain,7,2)
  823. tgheight=c2d(reverse(l2))
  824.  
  825.    l1=susbtr(ain,9,1)
  826.  ccwidth=c2d(l1)
  827.    l2=substr(ain,10,1)
  828.  ccheight=c2d(l2)
  829.  
  830.   l1=substr(ain,11,1)
  831.     tfore=c2d(l1)
  832.   l2=substr(ain,12,1)
  833.     tback=c1d(l2)
  834.  
  835. lena=length(ain);amess=''
  836.  do forever       
  837.     if iat>lena then return ""   /* no block terminator -- error */
  838.     iat=iat+1      /* size of block */
  839.     ii=substr(ain,iat,1) ; ii=c2d(ii)
  840.     if ii=0 then leave
  841.     iat=iat+1
  842.     amess=amess||substr(ain,iat,ii)
  843.     iat=iat+ii-1
  844.  end /* do */
  845.  
  846. return  tgleft tgtop tgwidth tgheight ccwidth ccheight tfore tback ','||amess
  847.  
  848.  
  849. /*******************/
  850. /* plain text stuff */
  851. make_pte_block;procedure
  852. parse arg tgleft,tgtop,tgwidth,tgheight,ccwidth,ccheight,tfore,tback,amess
  853.  
  854.   ab='2101'x
  855.   ab=ab||d2c(12)
  856.   ab=ab||dd2c(tgleft,2)
  857.   ab=ab||dd2c(tgtop,2)
  858.   ab=ab||dd2c(tgwidth,2)
  859.   ab=ab||dd2c(tgheight,2)
  860.   ab=ab||dd2c(ccwidth,1)
  861.   ab=ab||dd2c(ccheight,1)
  862.   ab=ab||dd2c(tfore,1)
  863.   ab=ab||dd2c(tback,1)
  864.   ab=ab||chunkit(amess)
  865.  
  866.   return ab
  867.  
  868. /*************/
  869. /* convert integer to character, using nb bytes */
  870. dd2c:procedure
  871. parse arg ival,nb
  872. if nb='' then nb=2
  873. a1=reverse(d2c(ival))
  874. if length(a1)<nb then do 
  875.    a1=a1||copies('00'x,nb-length(a1))
  876. end /* do */
  877. return left(a1,nb)
  878.  
  879.  
  880. /****************/
  881. /* convert character to interger */
  882.  
  883. /*******************/
  884.  
  885. /* make a terminator block -- no arguments needed
  886. Example:
  887.   my_trm_block=make_terminator_block()
  888.  
  889. */
  890. make_terminator_block:procedure
  891.  
  892. return '3b'x
  893.  
  894.  
  895. /*********************/
  896. /* parse a graphics control extension block (gce). 
  897.   Note: Use read_gif_block to get the gce.
  898.  
  899. Example:
  900.   imgnum=1
  901.   ablock=read_gif_block(giffile,imgnum,'GCE')
  902.   stu=read_gce_block(ablock)
  903.   parse var stu disposal userinputflag tcflag delay tcindex
  904.   say " disposal =  " disposal
  905.   say " userinput flag = " userinputflag
  906.   say " transparent color flag = " tcflag
  907.   say " Delay = " delay
  908.   say " transparent color index = " tcindex
  909.  
  910. */
  911.  
  912. read_gce_block:procedure
  913. parse arg ablock
  914.  
  915. l3=substr(ablock,4,1)
  916. l3=x2b(c2x(l3))
  917. reserved=left(l3,3)
  918. disposal=right(substr(l3,4,3),8,0)
  919. disposal=x2d(b2x(disposal))
  920. userinputflag=substr(l3,7,1)
  921. tcflag=substr(l3,8,1)
  922.  
  923. delay=c2d(reverse(substr(ablock,5,2)))
  924.  
  925. tcindex=c2d(substr(ablock,7,1))
  926.  
  927. return  disposal userinputflag tcflag delay tcindex
  928.  
  929.  
  930. /*****************/
  931. /* read lsd (including global color table), from string containing 
  932.    logical screen descriptor (lsd)
  933.    Note: use read_gif_block to get the lsd
  934.  
  935. Example of use:
  936.   ct2.=0
  937.   ct_name='CT2.'
  938.   st=read_lsd_block(gifcontents)
  939.   parse var st width height gcflag colres sort bkgcolor aspect
  940.   SAY "  # COLORS :" CT2.0
  941.   say " width " width
  942.   say " height " height
  943.   say " gcflag " gcflag
  944.   say " colres " colres
  945.   say " sort " sort
  946.   say " bkgcolor " bkgcolor
  947.   say " aspect " ASPECT
  948.   say " # colors = " ct_name.0 
  949.   do mm=0 to ct_name.0-1
  950.      say " Color " mm " ct_name.!r.mm ct_name.!g.mm ct_name.!b.mm
  951.   end 
  952.  
  953. */
  954.  
  955. read_lsd_block:procedure expose (ct_name)
  956. parse arg ain
  957.  
  958. /* organized as:
  959.  hd= 'GIFxxx' (1-6)
  960.  width = 2 bytes (7-8)
  961.  height=  2 bytes (9-10)
  962.  packed = 1 byte (11) -- gcflag (1) colres (3) sort (1) sizect (3)
  963.  bkgcolor =1 byte (12)
  964.  aspect = 1 byte (13)
  965.  colortable = 14 ... 13+ 2**(sizect+1)  bytes (rgbrgbrgb....)
  966. */
  967.  
  968. gifver=left(ain,6)
  969.  
  970. if abbrev(translate(gifver),'GIF8')=0 then do
  971.   return 'ERROR bad gif identifier: ' gifver
  972. end
  973.  
  974. l1=substr(ain,7,2)
  975. width=c2d(reverse(l1))
  976. l2=substr(ain,9,2)
  977. height=c2d(reverse(l2))
  978.  
  979. l3=substr(ain,11,1)  /* packed fields, used below */
  980.  
  981. bkg_color=c2d(substr(ain,12,1))
  982. aspect=c2d(substr(ain,13,1))
  983.  
  984. ctable0=x2b(c2x(l3))
  985.  
  986. global_color_flag=left(ctable0,1)
  987.  
  988. colres=substr(ctable0,2,3)
  989. colres=right(colres,8,0)
  990. colres=x2d(b2x(colres))
  991.  
  992. sort=substr(ctable0,5,1)
  993. ct1=right(ctable0,3)
  994.  
  995. ct1=right(ct1,8,0)
  996. ct1=x2d(b2x(ct1))
  997. numcolors=2**(ct1+1)
  998.  
  999. gcolortable=''
  1000. if global_color_flag=1 then do
  1001.    dcolortable=substr(ain,14,3*numcolors)
  1002.    ith=0
  1003.    do mm=1 to (numcolors*3) by 3
  1004.       aa=value(ct_name'!r.'ith,c2d(substr(dcolortable,mm,1)))
  1005.       aa=value(ct_name'!g.'ith,c2d(substr(dcolortable,mm+1,1)))
  1006.       aa=value(ct_name'!b.'ith,c2d(substr(dcolortable,mm+2,1)))
  1007.       ith=ith+1
  1008.    end
  1009. end
  1010. aa=value(ct_name'0',numcolors)
  1011. return  width height global_color_flag numcolors colres sort bkg_color aspect
  1012.  
  1013.  
  1014. /**************************
  1015. read_gif_block is called as:
  1016.  
  1017.      stuff=read_gif_block(gif_file,imgnum,infotype,is_string)
  1018.  
  1019. Parameters:
  1020.  
  1021.     GIF_FILE: Required. A fully qualified file name.
  1022.                   OR
  1023.               The contents of a gif_file (say, as read with 
  1024.                   gif_file=charin(afile,1,chars(afile))
  1025.  
  1026.          nth: # of image, etc. to get information about. If not specified,
  1027.               a value of 1 is assumed.
  1028.  
  1029.     infotype: Which type "descriptor block" to read (may be image specific)
  1030.               Actually, get the "nth" occurence of this infotype.
  1031.               Valid INFOTYPES are: LSD (nth will be ignored), GCE, IMG, PTE
  1032.               ACE, and CMT
  1033.  
  1034.     is_string: if 1,then gif_file is the "string" containing a gif file,
  1035.                 otherwise, gif_file is a file name.
  1036.  
  1037. Returns:
  1038.   A block from the gif file; or a string beginning with ERROR.
  1039.   Or, if infotype='', a list ob blocks in the gif_file.
  1040.  
  1041. Technical info:  For gif89a specs, please see
  1042.                  http://member.aol.com/royalef/gif89a.txt
  1043.  
  1044. */
  1045.  
  1046. read_gif_block:procedure
  1047. parse  arg afile,nth,infotype,is_string
  1048.  
  1049. infotype=translate(infotype)
  1050.  
  1051. if nth='' then nth=1
  1052.  
  1053. archy='LSD'    /* list of blocks found -- first is ALWAYS LSD block */
  1054. chewerr=0     /* flag set when error in chew_data */
  1055.  
  1056. /* read gif file ? */
  1057. if is_string<>1 then do
  1058.  fqn=stream(afile,'c','query exists')
  1059.  if fqn='' then do
  1060.     if chkerr=0 then return ''
  1061.     return 'ERROR no such file: ' afile
  1062.  end
  1063.  oo=stream(afile,'c','close')
  1064.  filesize=chars(afile)
  1065.  ain=charin(fqn,1,filesize)
  1066.  oo=stream(afile,'c','close')
  1067. end
  1068. else do         /* string provided */
  1069.    ain=afile
  1070. end
  1071.  
  1072. /* check for proper header */
  1073. gifver=left(ain,6)
  1074. if abbrev(translate(gifver),'GIF8')=0 then do
  1075.   if chkerr=0 then return ''
  1076.   return 'ERROR bad gif identifier: ' gifver
  1077. end
  1078.  
  1079.  
  1080. /* is there a global color table? */
  1081. l3=substr(ain,11,1)
  1082. ctable0=x2b(c2x(l3))
  1083. global_color_flag=left(ctable0,1)
  1084. ct1=right(ctable0,3)
  1085. ct1=right(ct1,8,0)
  1086. ct1=x2d(b2x(ct1))
  1087. numcolors=2**(ct1+1)
  1088.  
  1089. iat=13          /* 11 bytes used for intro info */
  1090.  
  1091. if global_color_flag=1 then do
  1092.    iat=iat+(3*numcolors)  /* iat is the Last byte used */
  1093. end
  1094.  
  1095. if infotype='LSD' then return substr(ain,1,iat)
  1096.  
  1097.  
  1098. /* if here, we need top scan file looking for other blocks */
  1099.  
  1100. desc.1='2c'x   /*'image' */
  1101. desc.2='21'x  /*'extension'*/
  1102. desc.3='3b'x   /*trailer' */
  1103.  
  1104. ext.1='f9'x ; /*graphic control'*/
  1105. ext.2='fe'x ; /*'comment'*/
  1106. ext.3='01'x ; /*'plain text'*/
  1107. ext.4='ff'x ; /*application'*/
  1108.  
  1109. nimgs=0         /* set counters */
  1110. ngcs=0
  1111. ncmts=0
  1112. napps=0
  1113. nptxts=0
  1114.  
  1115. lengif=length(ain)
  1116.  
  1117. do forever              /* ------------ scan the gif file */
  1118. iat=iat+1       
  1119.  
  1120. if iat>lengif then leave /* end of file contents (should not happen)*/
  1121.  
  1122. blockid=substr(ain,iat,1)       /* get next block type */
  1123. iat_b=iat               /* iat at beginning of this block */
  1124.  
  1125. select
  1126.  
  1127.    when blockid='00'x then do  /* ignore this relatively harmless error */
  1128.        ares=''
  1129.        ARCHY=archy' 00'
  1130.    end /* do */
  1131.  
  1132.    when blockid=desc.1 then do  /* it's an image */
  1133.       nimgs=nimgs+1
  1134.       call do_image
  1135.       ares=result
  1136.       archy=archy' IMG'
  1137.       if nimgs=nth  & infotype='IMG' then 
  1138.            return substr(ain,iat_b,(1+iat-iat_b))
  1139.    end
  1140.  
  1141.    when blockid=desc.2 then  do      /* extension */
  1142.        iat=iat+1                /* get extention type */
  1143.        extype=substr(ain,iat,1)
  1144.  
  1145.        select                   /* several types of "extensions */
  1146.  
  1147.           when extype=ext.1 then do     /*graphics control */
  1148.             ngcs=ngcs+1
  1149.             call graphics_control
  1150.             ares=result
  1151.             archy=archy' GCE'
  1152.             if infotype='GCE' & nth=ngcs then 
  1153.                return substr(ain,iat_b,(1+iat-iat_b))
  1154.           end
  1155.  
  1156.           when extype=ext.3  then do    /*plain text */
  1157.               nptxts=nptxts+1
  1158.               call plain_text  
  1159.               ares=result
  1160.               archy=archy' PTE'
  1161.               if nptxts=nth & infotype='PTE' then       /* check this image */
  1162.                     return substr(ain,iat_b,(1+iat-iat_b))
  1163.           end /* plain text */
  1164.  
  1165.           when extype=ext.2 then do     /*comment */
  1166.              ncmts=ncmts+1
  1167.              call is_comment
  1168.              ares=result
  1169.              archy=archy' CMT'
  1170.              if ncmts=nth &  infotype='CMT' then
  1171.                 return substr(ain,iat_b,(1+iat-iat_b))
  1172.           end
  1173.  
  1174.           when extype=ext.4 then do     /* application */
  1175.              napps=napps+1
  1176.              call application_block
  1177.              ares=result
  1178.              archy=archy' ACE'
  1179.              if nth=napps & infotype='ACE' then 
  1180.                 return substr(ain,iat_b,(1+iat-iat_b))
  1181.           end /* do */
  1182.  
  1183.           otherwise  do
  1184.              return 'ERROR Bad extension code: '||c2x(extype)
  1185.           end
  1186.        end      /* extype select */
  1187.    end          /* extention descriptor */
  1188.  
  1189.    when blockid=desc.3 then do
  1190.       archy=ARCHY' TRM'
  1191.       leave      /* terminator -- must be end of real gif stuff */
  1192.    end
  1193.  
  1194.    otherwise do
  1195.       return 'ERROR Bad descriptor code: ' blockid
  1196.    end
  1197.  
  1198. end  /* select */
  1199.  
  1200. if ares<>'' then do     /* ERROR DETECTED */
  1201.    if chkerr=0 then return ''
  1202.    return 'ERROR 'ares
  1203. end
  1204.  
  1205. end     /* forever */
  1206.  
  1207. /* if here, end of file and either nothing found, or found list of blocks */
  1208. if infotype='' then return archy
  1209. return ''                       /* blank means " not found " */
  1210.  
  1211.  
  1212. /************/
  1213. do_image:                             
  1214.       l3=substr(ain,iat+9,1)
  1215.       ctable0=x2b(c2x(l3))
  1216.       lcl_ct_flag=left(ctable0,1)
  1217.       t1=right(ctable0,3) ; t1=right(t1,8,0)
  1218.       lcl_ct_size=x2d(b2x(t1)) ; lcl_ct_size=2**(lcl_ct_size+1)
  1219.  
  1220.       skip=lcl_ct_flag*lcl_ct_size*3
  1221.       iat=iat+9+skip    /* iat is now just before the table based image */
  1222.  
  1223. /* chew up the data block */
  1224.        iat=iat+1        /* skip the lzw bits variable */
  1225.        img_data=chew_data()
  1226.        if imgdata="" then return 'ERROR Bad Image Data '
  1227.        return ""
  1228.  
  1229. /*********/
  1230. graphics_control:
  1231.        iat=iat+6
  1232.        term=x2d(c2x(substr(ain,iat,1)))
  1233.        if term<>0 then return 'Bad Graphics Control Extension '
  1234. return ""
  1235.  
  1236. /*********/
  1237. application_block:
  1238. iat=iat+1
  1239. app_blocksize=x2d(c2x(substr(ain,iat,1)))
  1240. if app_blocksize<>11 then do
  1241.     return 'Bad application block size '
  1242. end /* do */
  1243.  
  1244. iat=iat+11
  1245. app_data=chew_data()
  1246. if app_data="" then return 'Bad application block data '
  1247.  
  1248. return ""
  1249.  
  1250. /***********/
  1251. plain_text:
  1252. iat=iat+1
  1253. pt_data=''
  1254. app_blocksize=x2d(c2x(substr(ain,iat,1)))
  1255. if ptextblocksize<>12 then do
  1256.     return 'Bad Plain Text Block Size '
  1257. end /* do */
  1258.  
  1259. iat=iat+13
  1260. pt_data=chew_data()
  1261. if pt_data="" then return 'Bad Plain Text Data '
  1262. return ""
  1263.  
  1264. /*********/
  1265. is_comment:
  1266. cmt_data=chew_data()
  1267. if chewerr=1 then return 'Bad Comment Data '
  1268. return ""
  1269.  
  1270. /*********/
  1271. chew_data:procedure expose iat ain amess filesize chewerr
  1272. parse arg averbose
  1273.        chewerr=1
  1274.        amess=''
  1275.        do forever       /* data blocks */
  1276.          if iat>filesize then do
  1277.              return ""
  1278.          end /* do */
  1279.          iat=iat+1      /* size of block */
  1280.          ii=substr(ain,iat,1) ; ii=c2d(ii)
  1281.          if ii=0 then do 
  1282.              leave
  1283.          end /* do */
  1284.          iat=iat+1
  1285.          amess=amess||substr(ain,iat,ii)
  1286.          iat=iat+ii-1
  1287.        end /* do */
  1288. chewerr=0
  1289. return amess 
  1290.  
  1291. /***********/
  1292. /* make a chewable chunk of data */
  1293. chunkit:procedure
  1294. parse arg astr,klen
  1295. if klen='' then klen=250
  1296.  
  1297. mkit=''
  1298. lenstr=length(astr)
  1299. do mm=1 to lenstr by 250 
  1300.    iget=min(250,1+lenstr-mm)
  1301.    a1=substr(astr,mm,iget)
  1302.    a0=d2c(iget)
  1303.    mkit=mkit||a0||a1
  1304. end
  1305. mkit=mkit||'00'x   
  1306. return mkit
  1307.  
  1308.  
  1309.  
  1310.  
  1311.  
  1312.