Blame view

src/idl_extern/CMTotal_for_Dustemwrap/cmrestore.pro 25.2 KB
517b8f98   Annie Hughes   first commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
;+
; NAME:
;   CMRESTORE
;
; AUTHOR:
;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
;   craigm@lheamail.gsfc.nasa.gov
;
; PURPOSE:
;   Restore variables from an IDL SAVE file.
;
; CALLING SEQUENCE: (various)
;   CMRESTORE, filename                           (implicit)
;   CMRESTORE, filename, var1, var2, ..., [, NAMES=names]
;   CMRESTORE, filename, DATA=pointers, NAMES=names, PASS_METHOD='POINTER'
;   CMRESTORE, filename, DATA=handles,  NAMES=names, PASS_METHOD='HANDLE'
;   CMRESTORE, filename, DATA=structure,             PASS_METHOD='STRUCT'
;   
; DESCRIPTION: 
;
;   CMRESTORE is a replacement for the built-in IDL procedure RESTORE.
;   It restores variables and data from an existing IDL SAVE file,
;   written either by SAVE or CMSAVE.  The CMSV utility library must
;   be installed in your IDL path to use CMSAVE and CMRESTORE.
;
;   The primary advantage to CMRESTORE is the ability to selectively
;   restore only certain variables from the input file (based on
;   name).  CMRESTORE provides a number of ways to pass the data
;   between routines, typically using a pointer or structure, which
;   avoids the unsafe practice of restoring variables in the caller's
;   namespace.  However, CMRESTORE can restore variables into the
;   caller's namespace, but users should be aware that this capacity
;   is somewhat limited in IDL versions 5.2 and below.
;
;
; ==================================================================
;   Research Systems, Inc. has issued a separate license intended
;   to resolve any potential conflict between this software and the
;   IDL End User License Agreement. The text of that license
;   can be found in the file LICENSE.RSI, included with this
;   software library.
; ==================================================================
;
; COMPATIBILITY:
;
;   -- File Format --
;
;   CMRESTORE should be able to read files written by SAVE and CMSAVE
;   from IDL version 4 to version 5.4.
;
;   CMRESTORE cannot restore objects, pointers, compressed files, or
;   data sets larger than 2 gigabytes.
;   
;   Data types available in newer versions of IDL, such as pointers
;   and long integers, will not be readable in older versions of IDL
;   which do not have those data types.
;
;   -- Calling Interface --
;
;   For the most part, all capabilities of CMRESTORE are available to
;   the user.  However, it should be noted that passing variables by
;   positional parameter is not available under IDL 4, unless NAMES is
;   used to name the variables explicitly.  Also, under IDL versions
;   5.2 and previous, it is not possible for CMRESTORE to restore
;   variables into the caller's name space if they are not yet
;   defined.
;
;   This procedure is part of the CMSVLIB SAVE library for IDL by
;   Craig Markwardt.  You must have the full CMSVLIB core package
;   installed in order for this procedure to function properly.
;
; INPUTS:
;
;   FILENAME - the name of the SAVE file.
;              Default: none, this parameter must be specified.
;
;   VAR{i} - The values to be restored.  By default the save name is
;            taken from the named variables that are passed.  These
;            default names can be overridden by using the NAMES
;            keyword.
;
;            If no variables are passed as positional parameters, they
;            can still be saved using the DATA keyword.  By invoking
;            CMRESTORE without positional parameters or DATA,
;            CMRESTORE automatically will attempt to restore the
;            variables to the calling routine's name space (the
;            "implicit" technique).
;
;               NOTE: in IDL 5.2 and below, user routines are not
;               allowed to *CREATE* new variables in the caller's name
;               space.  CMRESTORE may fail if the variable in
;               undefined in the caller.  Therefore you must define it
;               before calling CMRESTORE.  The safer practice is to
;               use the VAR{i} positional parameters, or the DATA
;               keyword.
;
; KEYWORDS:
;
;   FILENAME - the name of the SAVE file.  The positional FILENAME
;              parameter takes precedence over the keyword FILENAME
;              parameter.
;
;              NOTE that if you pass variables as positional
;              parameters, then the first parameter *must* be the file
;              name, and the FILENAME *keyword* will be ignored.
;
;   PASS_METHOD - a scalar string, describing the method of passing
;                 data between the caller and CMRESTORE.  The keyword
;                 can take the value 'ARGUMENT', 'POINTER', 'HANDLE'
;                 or 'STRUCT'.  A value of 'ARGUMENT' indicates that
;                 data values will be passed by command line argument,
;                 and is the default.  Other values are described
;                 below.
;
;   DATA - A list of data elements to be restored from the output
;          file.  The data elements can be one of the following,
;          depending on the value of PASS_METHOD.  The means of
;          extracting the data, and the method of naming each
;          variable, are also indicated.
;
;            * PASS_METHOD='POINTER': An array of pointers to the variables
;                    Data: pointed-to value     Name: from NAMES keyword
;            * PASS_METHOD='HANDLE':  An array of handles to the variables
;                    Data: pointed-to value     Name: from NAMES keyword
;            * PASS_METHOD='STRUCT':  A structure containing data to be saved
;                    Data: tag value            Name: tag name
;
;          Data values are restored one by one, using the appropriate
;          name.  Note that any variables passed as positional
;          parameters will cause the DATA keyword to be ignored.
;
;          CMRESTORE will allocate any pointer or handle resources.
;          The calling routine is responsible for deallocating any
;          pointer or handle resources.
;
;   NAMES - a string array, giving the names for each variable.  
;
;           If the data are passed by positional parameters, the names
;           are assigned according to the position of the parameter in
;           the procedure call.  
;
;           If the data are passed by an array of pointers or handles,
;           then the names are assigned according to the position of
;           the data in the array.  In this case there is no other way
;           to supply the variable name.  NAMES is required.
;
;           If the data are passed in a structure, then the names are
;           assigned according to the position of the data in the
;           structure.  The values specified in the names keyword
;           override the tag names.
;
;   STATUS - upon return, an integer indicating the status of the
;            operation.  A value of 1 indicates success, while 0
;            indicates failure.  A failure condition does not
;            necessarily indicate that an individual variable could
;            not be restored; use the VARSTATUS keyword to detect such
;            situations.
;
;   VARSTATUS - upon return, an integer array indicating the status of
;               the restore operation for each variable.  A value of 1
;               at position i in the array indicates success for the
;               ith variable, while a value of 0 indicates failure.
;
;   ERRMSG - upon return, a string indicating the status of the
;            operation.  The empty string indicates success, while a
;            non-empty string indicates failure and describes the
;            error condition.
;
;   QUIET - if set, then the error message is returned to the calling
;           routine.  By default an error condition causes execution
;           to stop and the message to be printed on the console.
;
;   VERBOSE - if set, then a short message is printed for each
;             variable.
;
; EXAMPLE:
;
;   CMSAVE, VAR1, VAR2, FILENAME='test.sav'
;   CMSAVE, VAR1, VAR2, FILENAME='test.sav', NAMES=['A','B']
;
;     Save the data in VAR1 and VAR2 to the file test.sav.  In the
;     first case the saved variable names will be VAR1 and VAR2.  In
;     the second case the saved variable names will be A and B.
;
;   POINTERS = [ptr_new(VAR1), ptr_new(VAR2)]
;   CMSAVE, DATA=POINTERS, NAMES=['A','B'], FILENAME='test.sav'
;
;     Save the data in VAR1 and VAR2 to the file test.sav.  The saved
;     variable names will be A and B.
;
;   STRUCTURE = {A: VAR1, B: VAR2}
;   CMSAVE, DATA=STRUCTURE, FILENAME='test.sav'
;
;     Save the data in VAR1 and VAR2 to the file test.sav.  The saved
;     variable names will be A and B.
;
; SEE ALSO:
;
;   CMSAVE, SAVE, RESTORE
;
; MODIFICATION HISTORY:
;   Written, 14 May 2000
;   Documented, 22 Sep 2000
;   Restore into caller's name space now permitted, 11 Jan 2001
;   Documented "implicit" restore a little better, w/ errors, 01 Mar 2001
;   Make version checks with correct precision, 19 Jul 2001, CM
;   Restore with no args automatically does ALL, is this right?,
;     CM, 20 Aug 2001
;   Added notification about RSI License, 13 May 2002, CM
;   Handle the case of CMRESTORE, FILENAME, X properly, 03 Sep 2008, CM
;     (thanks to Sergey Koposov for reporting)
;   Report CMSVLIB version number when /VERBOSE is set, 22 Nov 2009,
;      CM
;   Change to accomodate lack of GDL functionality when restoring
;      all variables, 22 Nov 2009, CM
;
; $Id: cmrestore.pro,v 1.22 2009/11/22 23:31:00 craigm Exp $
;
;-
; Copyright (C) 2000-2001, 2008, 2009, Craig Markwardt
; This software is provided as is without any warranty whatsoever.
; Permission to use, copy, modify, and distribute modified or
; unmodified copies is granted, provided this copyright and disclaimer
; are included unchanged.
;-

;; This utility function is only called under IDL 4
function arg_present, x
  return, 0
end

pro cmrestore,   filename0, filename=filename1, $
                  p0,  p1,  p2,  p3,  p4,  p5,  p6,  p7,  p8,  p9, $
                 p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, $
                 p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, $
                 verbose=verbose, all=all, restored_objects=restobj, $
                 status=status, varstatus=colstatus, mtimes=mtimes, $
                 names=names, data=data, pass_method=method, $
                 errmsg=errmsg, quiet=quiet, nocatch=nocatch, $
                 relaxed_structure_assignment=relax, version=revision
                  
  forward_function routine_names, ptrarr, ptr_new, handle_create, arg_present

  status = 0
  errmsg = ''
  colstatus = 0 & dummy = temporary(colstatus)  ;; Void out the status

  cmsvlib_version = '<unknown>'
  catch, catcherr
  if catcherr EQ 0 then lib = cmsvlib(/query, version=cmsvlib_version) else lib = 0
  catch, /cancel
  if lib EQ 0 then $
    message, 'ERROR: The CMSVLIB library must be in your IDL path.'

  revision = '$Revision: 1.22 $'
  ;; Extract the version number
  revision = stregex(revision,'\$'+'Revision: *([0-9.]+) *'+'\$',/extract,/sub)
  revision = revision(1)

  if keyword_set(verbose) then begin
     message, /info, 'CMRESTORE version '+revision, traceback=0
     message, /info, 'CMSV Library version '+cmsvlib_version(0), traceback=0
  endif

  if NOT keyword_set(nocatch) then on_error, 2
  kall = 0                      ;; NOTE: Ignoring ALL keyword!!

  n_par = n_params()
  nnames = n_elements(names)
  ver = double(!version.release)
  if n_elements(filename0) EQ 0 AND n_elements(filename1) EQ 0 then begin
      message, 'USAGE: CMRESTORE, filename, VAR1, VAR2, ...', /info
      return
  end
  ;; Now n_par refers to the number of positional variables (ie,
  ;; filename excluded)
  n_par = n_par - 1

  ;; Default processing
  if n_elements(filename0) GT 0 then filename = strtrim(filename0(0),2) $
  else                               filename = strtrim(filename1(0),2)

  ;; Input method
  if n_elements(method) EQ 0 then begin
      if n_par GT 0 then meth = 'ARGUMENT' $
      else               meth = 'STORE'
  endif else begin
      meth = strupcase(strtrim(method(0),2))
  endelse
  ;; Trim it down so that people don't have to type too many characters
  meth = strmid(meth, 0, 3)

  ;; Error checking on input method, depending on IDL version
  if meth EQ 'ARG' AND ver LT 5D AND nnames EQ 0 then begin
      errmsg = ('ERROR: in IDL 4 you cannot pass variables by argument '+ $
                'to CMRESTORE.')
      goto, PRE_CLEANUP
  endif
  ;; Implicit restore of ALL, if no names are provided
  if nnames EQ 0 AND n_par EQ 0 then kall = 1
  ;; GDL is incapable of restoring all variables without them being named.
  DEFSYSV, '!gdl', exists=is_it_gdl
  if is_it_gdl then begin
      if nnames EQ 0 then kall = 1
  endif

  ;; Extract some information about the calling routine
  lev = routine_names(/level)
  vlev = lev - 1

  ;; Note: if the user specified "ALL" then we set NNAMES to zero,
  ;; indicating that the number of variables to be returned is unknown
  ;; at present.
  if kall then nnames = 0L

  if n_par LT 1 AND meth NE 'STO' AND arg_present(data) EQ 0 then begin
      if ver GE 5D OR meth EQ 'ARG' then begin
          errmsg = ('ERROR: A way to return data must be specified.  Pass by'+$
                    ' argument (IDL >= 5), or using the NAMES/DATA keywords.')
          goto, PRE_CLEANUP
      endif
  endif

  ;; ---------------- Establish I/O parameters -------------------
  ;; Upon exit from this block, at least the following variables must
  ;; be set: (a) nvar = number of named variables, (b) vnames = names
  ;; of variables in SAVE file to be restored.
  
  ;; Special case: if keyword ALL is set, then nvar EQ 0, and vnames
  ;; is undefined.  It will be filled in later then.
  if meth EQ 'ARG' then begin
      ;; Now processing that depends on the data passing method.  The
      ;; "argument" method is via positional arguments.

      ;; Extract variables from positional parameters
      if kall EQ 0 then nnames = n_par
      if (kall EQ 0 AND nnames EQ 0) $
        OR (kall AND n_par LE 0) then begin
          errmsg = 'ERROR: Returnable variables must be specified.'
          goto, PRE_CLEANUP
      endif
      lev1 = strtrim(lev-1,2)
      nvar = nnames
      if nvar GT 0 then vnames = strarr(nvar)
      if NOT kall AND ver GE 5D then begin
          for i = 0L, nvar-1 do $
            dummy = execute('vnames(i) = (routine_names(p'+strtrim(i,2)+ $
                            ',arg_name='+lev1+'))(0)')
      endif
  endif else if meth EQ 'STO' then begin
      ;; This information is used to determine whether the variable
      ;; already exists in the caller.  If it doesn't, and we are in
      ;; IDL 5.2 or earlier, then we can't save it.
      cnames = strupcase(strtrim(routine_names(variables=vlev),2))

      nvar = nnames
      if nvar GT 0 then vnames = strarr(nvar)
      
  endif else begin

      ;; Instead of passing the data via positional parameters, they
      ;; can be set through the DATA keyword, but even there this can
      ;; be accomplished with several means: via pointers, via
      ;; handles, or using a structure.
      
      ;; Clear DATA in preparation for restore operation
      data = 0 & dummy = temporary(data)
      nvar = nnames
      if nvar GT 0 then vnames = strarr(nvar)

      if meth EQ 'POI' then begin               ;; POINTER TYPE
          ;; Construct an array of null pointers to start with
          if nvar GT 0 then data = ptrarr(nvar)
      endif else if meth EQ 'HAN' then begin     ;; HANDLE TYPE
          ;; Construct an array of invalid handles to start with
          if nvar GT 0 then data = lonarr(nvar)-1L
      endif else if meth EQ 'STR' then begin
          ;; Do nothing
      endif else begin
          errmsg = 'ERROR: PASS_METHOD must be one of ARGUMENT, POINTER, '+$
            'HANDLE or STRUCT'
          PRE_CLEANUP:
          status = 0
          if NOT keyword_set(quiet) then message, errmsg, /info
          return
      endelse
  endelse

  if kall EQ 0 then begin
      if nvar LE 0 then begin
          errmsg = 'ERROR: no variable names were specified'
          goto, PRE_CLEANUP
      endif else begin
          colstatus = lonarr(nvar)
      endelse
  endif

  ;; User-renamed variables.  These names will override any names
  ;; specified in positional parameters.
  if n_elements(names) GT 0 AND NOT kall then begin
      sz = size(names)
      if sz(sz(0)+1) NE 7 then begin
          errmsg = 'ERROR: NAMES must be a string array'
          goto, PRE_CLEANUP
      endif
      vnames(0) = strtrim(strupcase(names(*)),2)
  endif

  ;; Open the save file
  get_lun, unit
  cmsv_open, unit, filename, pp, access='R', status=status, errmsg=errmsg
  if status EQ 0 then goto, CLEANUP
  if keyword_set(verbose) then $
    message, 'Portable (XDR) SAVE/RESTORE file.', /info, traceback=0

  pp0 = pp  ;; Block pointer
  ivar = 0L ;; Number of variables that have been read successfully
  if n_elements(vnames) GT 0 then $
    found = lonarr(n_elements(vnames))

  ptr_index   = [0L]
  ptr_offsets = [0L]
  if ver GE 5D then ptr_data = [ptr_new()] $
  else              ptr_data = [0L]

  ;; Now begin the processing
  repeat begin
      ;; Read block from SAVE file
      bn = ''
      point_lun, unit, pp
      block = 0 & dummy = temporary(block)
      cmsv_rrec, block, pp1, bdata, unit=unit, next_block=pnext, /init, $
        block_type=bt, block_name=bn, status=status, errmsg=errmsg, offset=pp,$
        promote64=promote64
      if status EQ 0 then goto, CLEANUP
      eb = (bn EQ 'END_MARKER')

      ;; Examine each block type  -----

      errmsg = ''
      jfind = -1L
      case bn of 

          ;; Promote record header to 64-bits (compatibility)
          'PROMOTE64': if keyword_set(verbose) then begin
              message, 'File contains 64-bit offsets.', /info, traceback=0
          endif

          ;; Read time stamp record
          'TIMESTAMP': if keyword_set(verbose) then begin
              stamp = bdata
              message, 'Save file written by '+stamp.save_user+'@'+ $
                stamp.save_host+', '+stamp.save_date, /info, traceback=0
          endif
          
          ;; Read version record
          'VERSION': if keyword_set(verbose) then begin
              vers = bdata
              message, ('IDL version '+vers.release+' ('+vers.os+ $
                        ', '+vers.arch+')'), /info, traceback=0
              message, 'File format revision: '+ $
                strtrim(vers.format_version,2), /info, traceback=0
          endif

          ;; Read heap index record
          'HEAP_INDEX': begin
              ii = bdata
              ptr_index   = [ptr_index, ii]
              ptr_offsets = [ptr_offsets, ii*0L]
              if ver GE 5D then $
                ptr_data    = [ptr_data, ptrarr(n_elements(ii))]
          end

          ;; Read heap data - just store a file pointer for later
          ;;                  referral
          'HEAP_DATA': begin
              p2 = pp1
              cmsv_rvtype, block, pp1, vindex, /heap, unit=unit, $
                status=st1, errmsg=errmsg
              if st1 EQ 0 then goto, NEXT_BLOCK

              ;; VINDEX will be the heap variable number.  Once we
              ;; know this we can put the file offset into
              vindex = floor(vindex(0))
              wh = where(ptr_index EQ vindex, ct)
              if ct EQ 0 then goto, NEXT_BLOCK
              ptr_offsets(wh(0)) = pp + p2  ;; block address + offset
          end              

          ;; Read variable data, and store for return to caller
          'VARIABLE': begin
              ;; Read variable type
              cmsv_rvtype, block, pp1, vn, sz1, unit=unit, status=st1, $
                template=tpp1, errmsg=err1
              if vn EQ '' OR st1 EQ 0 then goto, NEXT_BLOCK
              if kall EQ 0 then begin
                  jfind = (where(vn EQ vnames, ct))(0)
                  if ct EQ 0 then goto, NEXT_BLOCK
                  found(jfind) = 1
              endif
              
              ;; Read variable data
              cmsv_rdata, block, pp1, sz1, val, template=tpp1, status=st1, $
                unit=unit, errmsg=errmsg, ptr_offsets=ptr_offsets, $
                ptr_index=ptr_index, ptr_data=ptr_data
              if st1 EQ 0 then goto, NEXT_BLOCK
              if sz1(0) GT 0 then arr = 1 else arr = 0
              if arr then begin
                  ;; If an array then reform to be sure dimensions are right
                  dims = sz1(1:sz1(0))
                  val = reform(val, dims, /overwrite)
              endif
              
              if kall then begin
                  ;; With ALL, we extend the vector at each variable
                  jfind = ivar & ii = strtrim(jfind,2)
                  if ivar EQ 0 then begin
                      anames = [vn]
                      colstatus = [0L]
                  endif else begin
                      anames = [anames, vn]
                      colstatus = [colstatus, 0L]
                  endelse
              endif
              
              ;; Now send the data to output, depending on the method
              case meth of 
                  'ARG': begin
                      ;; Position dependent parameter
                      if jfind GE n_par then goto, NEXT_BLOCK
                      ii = strtrim(jfind,2)
                      if arr EQ 0 then begin
                          dummy = execute('p'+ii+' = temporary(val)') 
                      endif else begin
                          dummy = execute('p'+ii+' = reform(val, dims, '+ $
                                          '/overwrite)')
                      endelse
                      if dummy EQ 0 then goto, NEXT_BLOCK
                  end
                  'STO': begin
                      ;; Store the data in caller.  Check for IDL 5.3
                      ;; compatibility.
                      jfind1 = where(vn EQ cnames, ct1)
                      if ver LT 5.3D AND jfind1(0) EQ -1 then begin
                          if keyword_set(quiet) EQ 0 then begin
                              message, 'WARNING: could not create variable '+$
                                vn+' in calling routine.', /info, traceback=0
                          endif
                          goto, NEXT_BLOCK
                      endif
                      dummy = routine_names(vn, val, store=vlev)
                  end
                  'POI': begin
                      ;; Pointer to data
                      if kall then begin
                          if ivar EQ 0 then data = ptrarr(1) $
                          else data = [data, ptr_new()]
                      endif
                      data(jfind) = ptr_new(val)
                  end
                  'HAN': begin
                      ;; Handle to data
                      if kall then begin
                          if ivar EQ 0 then data = [-1L] $
                          else data = [data, -1L]
                      endif
                      data(jfind) = handle_create(value=val, /no_copy)
                  end
                  'STR': begin
                      ;; Add data to structure
                      if n_elements(sdata) EQ 0 then $
                        sdata = create_struct(vn, val) $
                      else $
                        sdata = create_struct(sdata, vn, val)
                  end
              endcase
              
              colstatus(jfind) = 1
              if kall then ivar = ivar + 1
          end


          ELSE: dummy = 1
      endcase

      NEXT_BLOCK:
      if NOT keyword_set(quiet) then begin
          if errmsg NE '' then $
            message, errmsg, /info
          if keyword_set(verbose) AND jfind GE 0 then begin
              if colstatus(jfind) EQ 1 then $
                message, 'Restored variable: '+vn+'.', /info, traceback=0 $
              else $
                message, 'Unable to restore variable: '+vn+'.', /info, $
                traceback=0
          endif
      endif
      ;; Advance to next block
      if pp NE pnext then begin
          pp = pnext 
      endif else begin
          status = 0
          errmsg = 'ERROR: internal inconsistency'
      endelse
  endrep until bn EQ 'END_MARKER' OR status EQ 0

  ;; Clean up
  free_lun, unit

  DONE_RESTORE:
  if meth EQ 'STR' then begin
      ;; Put new struct data on output
      data = 0 & dummy = temporary(data)
      if n_elements(sdata) GT 0 then data = temporary(sdata)
  endif
  if kall then begin
      names = 0 & dummy = temporary(names)
      if n_elements(anames) GT 0 then names = anames
  endif

  if n_elements(found) GT 0 then begin
      wh = where(found EQ 0, ct)
      fmt = '(A,'+strtrim(ct,2)+'(A,:," "),".")'
      if ct GT 0 then begin
          errmsg = string("WARNING: the following variables were not found: ",$
                          vnames(wh), format=fmt)+'.'
          if NOT keyword_set(quiet) OR keyword_set(verbose) then $
            message, errmsg, /info, traceback=0
      endif
  endif

  if total(colstatus) GT 0 then begin
      status = 1
  endif else begin
      status = 0
      errmsg = 'ERROR: No variables were restored.'
      if meth EQ 'STO' AND ver LT 5.3D then begin
          errmsg = errmsg + '  NOTE: In IDL 5.2 and earlier the variable '+$
            'must first be defined by the caller in order to use the '+$
            '"implicit" restore technique.'
          message, errmsg, /info, traceback=0
      endif
  endelse

  return

  ;; 
  ;; Error handling routine, outside the normal call flow
  CLEANUP:
  catch, /cancel
  if n_elements(unit) GT 0 then free_lun, unit
  status = 0
  if errmsg NE '' AND NOT keyword_set(quiet) then message, errmsg, /info

  return
end