c
c     Copyright (c) 1999,2000, Compaq Computer Corporation
c
c     See the file "license.terms" for information on usage and redistribution
c     of this file, and for a DISCLAIMER OF ALL WARRANTIES.
c
c   Subroutines to read an ASCII data file for input into Postmini
c
      subroutine read_ascii_file
      implicit none
c
      include 'const.h'
      include 'sysdep.cmn'
      include 'io.cmn'
      include 'user.cmn'
c
c     read an ASCII data file (1D/2D)
c
c     locals
c
c     Menu code items
      character*80 string
      character*40 str, colerr, colerr2
      real         real_vec(1)
      integer      i, item_number, int_vec(MAXCOLS),
     1             imax, imin, ncols, string_len, ilen
      logical      eof, do_read, delayed_message, read_error, lerr,
     1             lpure_1d, have_x_expr, have_y_expr, use_last_input
c
c     Intialize
      if( string_len(user_1d_asciifile) .eq. 0 ) then
         user_1d_nlines = 0
         user_1d_n_ycols = 1
         user_1d_skip   = 0
         user_1d_sweep  = .FALSE.
         user_1d_col_x  = 1
         user_1d_col_y(1) = 2
         user_1d_n_ycols  = 1
         user_1d_col_z  = 3
         user_1d_dim    = 1
         user_1d_sample_freq = 1
         user_1d_dataset_number = 1
         user_1d_ax     = 0.0
         user_1d_ay     = 0.0
         user_1d_az     = 0.0
         user_1d_mx     = 1.0
         user_1d_my     = 1.0
         user_1d_mz     = 1.0
         user_1d_xabs   = .FALSE.
         user_1d_yabs   = .FALSE.
         user_1d_zabs   = .FALSE.
         user_1d_contour = .FALSE.
         user_1d_errorbars = .FALSE.
         user_1d_x_char_data = .FALSE.
         user_1d_rect_grid = .TRUE.
         user_1d_x_expression = ' '
         user_1d_y_expression = ' '
         user_1d_merge_ycols  = .FALSE.
      endif
c
      if( string_len(user_1d_x_expression) .ne. 0 ) then
         have_x_expr = .TRUE.
      else
         have_x_expr = .FALSE.
      endif
      if( string_len(user_1d_y_expression) .ne. 0 ) then
         have_y_expr = .TRUE.
      else
         have_y_expr = .FALSE.
      endif
c
c     Until these attributes can be set by the user interactively,
c     initialize them to innocuous values.
      user_1d_sort   = .FALSE.
c
c     Format a common error message
      write(colerr,10) MAXCOLS
10    format('Column must be between 1 and ',i5)
      write(colerr2,11) MAXCOLS
11    format('Column(s) must be between 1 and ',i5)
c
      user_1d_asciifile = binfilename
      do_read = .FALSE.
      use_last_input = .FALSE.
c
100   call clear_screen
      call display_string(1, 1,
     1     '  Read ASCII data file (in column format)',41,0)
      call display_string(3 ,1 ,' 1) File :',10,0)
      call display_string(4 ,1 ,' 2) X column      :',19,0)
      call display_string(5 ,1 ,' 3) Y column(s)   :',19,0)
      call display_string(6 ,1 ,' 4) Z column      :',19,0)
      call display_string(7 ,1 ,' 5) Datafile type :',19,0)
      call display_string(8 ,1 ,' 6) Lines to skip :',19,0)
      call display_string(9 ,1 ,' 7) Lines to read :',19,0)
      call display_string(9 ,27,' (0 == read entire file)',25,0)
      call display_string(10,1 ,' 8) Sample frequency :',22,0)
      call display_string(10,27,
     1     ' (1=read each point, 2=read every other point...)',50,0)
      call display_string(11,1 ,' 9) Dataset       :',19,0)
      call display_string(11,27,' (SPICE output only)',20,0)
      call display_string(12,1 ,'10) Multiple curves in datafile :',
     1                    33,0)
      call display_string(13,1 ,'11) X coord offset (AX) :',25,0)
      call display_string(14,1 ,'12) X coord scale  (MX) :',25,0)
      call display_string(15,1 ,'13) Y coord offset (AY) :',25,0)
      call display_string(16,1 ,'14) Y coord scale  (MY) :',25,0)
      call display_string(17,1 ,'15) Z coord offset (AZ) :',25,0)
      call display_string(18,1 ,'16) Z coord scale  (MZ) :',25,0)
      call display_string(19,1 ,'17) X coord expression  :',25,0)
      call display_string(20,1 ,'18) Y coord expression  :',25,0)
      call display_string(13,40,'19) Data on rectangular grid : ',31,0)
      call display_string(14,40,'20) Merge all Y column data  : ',31,0)
c
      call display_string(3,12,user_1d_asciifile,69,1)
      call display_int   (4,21,user_1d_col_x,3)
c     
      call display_string(5,21,' ',60,1)
      if( user_1d_col_y(1) .eq. -1 ) then
         call display_string(5,21,'ALL',60,1)
      else
         call display_ints(user_1d_col_y,user_1d_n_ycols,5,21,3)
      endif
c
      if( user_1d_dim .eq. 1 .and. .not.
     1    (user_1d_contour .or. user_1d_errorbars) ) then
         call display_string(6,21,' NA',3,1)
      else
         call display_int   (6,21,user_1d_col_z,3)
      endif
      if( user_1d_dim .eq. 1 ) then
         if( user_1d_contour ) then
            str = 'X-Y (with z-sort)'
         else if( user_1d_errorbars ) then
            str = 'X-Y (with error bars)'
         else if( user_1d_x_char_data ) then
            str = 'X-Y (X character data)'
         else
            str = 'X-Y'
         endif
      else if( user_1d_dim .eq. 2 ) then
         str = 'X-Y-Z'
      endif
      call display_string (7,21,str,40,1)
      call display_int   (8,21,user_1d_skip, 5)
      call display_int   (9,21,user_1d_nlines, 5)
      call display_int   (10,24,user_1d_sample_freq,3)
      if( user_1d_dim .eq. 1 ) then
         call display_int(11,21,user_1d_dataset_number,5)
      else
         call display_string (11,21,'   NA',5,1)
      endif
      if( user_1d_sweep ) then
         str = 'yes'
      else
         str = ' no'
      endif
      call display_string(12,35,str,3,1)
      if( have_x_expr ) then
         call display_string(13,26,'       NA',9,1)
         call display_string(14,26,'       NA',9,1)
      else
         call display_real(13,26,user_1d_ax,1)
         call display_real(14,26,user_1d_mx,1)
      endif
      if( have_y_expr ) then
         call display_string(15,26,'       NA',9,1)
         call display_string(16,26,'       NA',9,1)
      else
         call display_real(15,26,user_1d_ay,1)
         call display_real(16,26,user_1d_my,1)
      endif
      call display_real(17,26,user_1d_az,1)
      call display_real(18,26,user_1d_mz,1)
c
      ilen = string_len(user_1d_x_expression)
      if( ilen .ne. 0 ) then
         call display_string(19,27,user_1d_x_expression,52,1)
      else
         call display_string(19,27,'(none)',6,1)
      endif
c
      ilen = string_len(user_1d_y_expression)
      if( ilen .ne. 0 ) then
         call display_string(20,27,user_1d_y_expression,52,1)
      else
         call display_string(20,27,'(none)',6,1)
      endif
c
      if( user_1d_dim .eq. 1 ) then
         str = ' NA'
      else if( user_1d_rect_grid ) then
         str = 'yes'
      else
         str = ' no'
      endif
      call display_string(13,71,str,3,1)
c
      if( user_1d_merge_ycols ) then
         str = 'yes'
      else
         str = ' no'
      endif
      call display_string(14,71,str,3,1)
c
c     Display any messaged from before window refresh
      if( delayed_message ) then
         call display_message(string)
         delayed_message = .FALSE.
      endif
c
200   call display_prompt('Enter item number, ' //
     1   'or Repaint, Accept (read file), Preview, Quit (don''t read)')
      if( .NOT. use_last_input ) then
         call read_from_input_area(string, eof)
      endif
      if( eof ) goto 200
      call upcase(string)
      if( string(1:1) .eq. 'A') then
         do_read = .TRUE.
         goto 300
      else if( string(1:1) .eq. 'Q') then
         do_read = .FALSE.
         goto 300
      elseif (string(1:1) .eq. 'R' ) then
         goto 100
      elseif (string(1:1) .eq. 'P' ) then
         call preview_ascii_file(user_1d_asciifile,user_1d_skip,
     1                           user_1d_dataset_number,read_error)
         if( read_error ) then
            delayed_message = .TRUE.
            string = 'Error occurred during open or read of file'
         endif
         goto 100
      else
         read(string,*,err=1000) item_number
         if ( item_number .eq. 1 ) then
c           Filename
            call get_string_with_prompt(user_1d_asciifile,
     1           'Enter filename')
            call display_string(3,12,user_1d_asciifile,69,1)
         else if ( item_number .eq. 2 ) then
c           X column
            call get_ints_with_prompt(int_vec, 1,
     1         'Enter column which has absissa (X)')
            if( int_vec(1) .lt. 1 .or. int_vec(1) .gt. MAXCOLS ) then
               call display_message(colerr)
            else
               user_1d_col_x = int_vec(1)
               call display_int(4,21,user_1d_col_x,3)
            endif
         else if ( item_number .eq. 3 ) then
c           Y column(s)
            if( user_1d_dim .gt. 1 .or. user_1d_contour .or.
     1          user_1d_errorbars ) then
               lpure_1d = .FALSE.
            else
               lpure_1d = .TRUE.
            endif
            if( lpure_1d ) then
               call display_prompt(
     1         'Enter column(s) which have ordinate (Y), ' //
     2         'use ALL to load all remaining columns')
            else
               call display_prompt(
     1         'Enter column which has ordinate (Y)')
            endif
            call read_from_input_area(string, eof)
            if( .NOT. eof ) then
               call upcase(string)
               call parse_columns(string,int_vec,ncols,
     1                            MAXCOLS,lerr)
               if( lerr ) then
                  call display_message('Invalid column(s) specified')
               else if( lpure_1d ) then
                  imax=0
                  imin=MAXCOLS+1
                  do i=1,ncols
                     imax = MAX(imax,int_vec(i))
                     imin = MIN(imin,int_vec(i))
                  end do
                  if( int_vec(1) .eq. -1 .and.
     1                ncols .eq. 1) then
c                    This is the hacky way we specify "ALL" columns for Y
                     user_1d_n_ycols  =  1
                     user_1d_col_y(1) = -1
                     call display_string(5,21,'ALL',60,1)
                     user_1d_sweep = .TRUE.
                     str = 'yes'
                     call display_string(12,35,str,3,1)
                  else if( imin .lt. 1 .or. imax .gt. MAXCOLS ) then
                     call display_message(colerr2)
                  else
                     user_1d_n_ycols = ncols
                     do i=1,user_1d_n_ycols
                        user_1d_col_y(i) = int_vec(i)
                     end do
                     call display_string(5,21,' ',60,1)
                     call display_ints(user_1d_col_y,user_1d_n_ycols,
     1                                 5,21,3)
c                    Set sweep mode if number of columns > 1 or ALL cols
                     if(ncols .gt. 1 .or. user_1d_col_y(1) .eq. -1) then
                        user_1d_sweep = .TRUE.
                        str = 'yes'
                        call display_string(12,35,str,3,1)
                     endif
                  endif
               else
                  if( int_vec(1) .lt. 1 .or.
     1                int_vec(1) .gt. MAXCOLS ) then
                     call display_message(colerr)
                  else
                     user_1d_n_ycols = ncols
                     user_1d_col_y(1) = int_vec(1)
                     call display_string(5,21,' ',60,1)
                     call display_ints(user_1d_col_y,user_1d_n_ycols,
     1                                 5,21,3)
                     if( user_1d_dim .gt. 1 ) then
                        user_1d_sweep = .FALSE.
                        str = ' no'
                        call display_string(12,35,str,3,1)
                     endif
                  endif
               endif
            endif
         else if ( item_number .eq. 4 ) then
c           Z column
            if( user_1d_dim .eq. 1 .and.
     1          .not. (user_1d_contour .or. user_1d_errorbars) ) then
               call display_message(
     1              'Z column not applicable in X-Y mode')
            else
               call get_ints_with_prompt(int_vec, 1,
     1            'Enter column which has Z data?')
               if( int_vec(1) .lt. 1 .or. int_vec(1) .gt. MAXCOLS ) then
                 call display_message(colerr)
               else
                 user_1d_col_z = int_vec(1)
                 call display_int(6,21,user_1d_col_z,3)
               endif
            endif
         else if ( item_number .eq. 5 ) then
c           Datafile type
            if( use_last_input .eq. .FALSE. ) then
c              This call will wipe out the current screen,
c              so refresh it...
               call pm_get_ascii_datafile_type(int_vec(1))
               use_last_input = .TRUE.
               goto 100
            else
               use_last_input = .FALSE.
            endif
            if( int_vec(1) .lt. 1 .or. int_vec(1) .gt. 5 ) then
               call display_message('Type must be 1, 2, 3, 4 or 5')
            else
               if( int_vec(1) .eq. 1 ) then
                  user_1d_dim = 1
                  user_1d_contour = .FALSE.
                  user_1d_errorbars = .FALSE.
                  user_1d_x_char_data = .FALSE.
                  str = 'X-Y'
                  call display_string(6,21,' NA',3,1)
                  call display_string(19,35,' NA',3,1)
               else if( int_vec(1) .eq. 2 ) then
                  user_1d_dim = 1
                  user_1d_contour = .TRUE.
                  user_1d_errorbars = .FALSE.
                  user_1d_x_char_data = .FALSE.
                  str = 'X-Y (with z-sort)'
                  call display_int(6,21,user_1d_col_z,3)
                  call display_string(19,35,' NA',3,1)
               else if( int_vec(1) .eq. 3 ) then
                  user_1d_dim = 2
                  user_1d_contour = .FALSE.
                  user_1d_errorbars = .FALSE.
                  user_1d_x_char_data = .FALSE.
                  call display_int(6,21,user_1d_col_z,3)
                  if( user_1d_rect_grid ) then
                     str = 'yes'
                  else
                     str = ' no'
                  endif
                  call display_string(13,71,str,3,1)
                  str = 'X-Y-Z'
               else if( int_vec(1) .eq. 4 ) then
                  user_1d_dim = 1
                  user_1d_contour = .FALSE.
                  user_1d_errorbars = .TRUE.
                  user_1d_x_char_data = .FALSE.
                  str = 'X-Y (with error bars from Z column)'
                  call display_int(6,21,user_1d_col_z,3)
                  call display_string(19,35,' NA',3,1)
               else if( int_vec(1) .eq. 5 ) then
                  user_1d_dim = 1
                  user_1d_contour = .FALSE.
                  user_1d_errorbars = .FALSE.
                  user_1d_x_char_data = .TRUE.
                  str = 'X-Y (X has non-blank character data)'
                  call display_string(6,21,' NA',3,1)
                  call display_string(19,35,' NA',3,1)
               endif
               call display_string (7,21,str,40,1)
            endif
         else if ( item_number .eq. 6 ) then
c           Lines to skip
            call get_ints_with_prompt(int_vec, 1,
     1         'Enter number of lines to skip')
            if( int_vec(1) .lt. 0 ) then
               call display_message('Number must be non-negative')
            else
               user_1d_skip = int_vec(1)
               call display_int(8,21,user_1d_skip,5)
            endif
         else if ( item_number .eq. 7 ) then
c           Lines to read
            call get_ints_with_prompt(int_vec, 1,
     1         'Enter number of lines to read (0 == read entire file)')
            if( int_vec(1) .lt. 0 ) then
               call display_message('Number must be non-negative')
            else
               user_1d_nlines = int_vec(1)
               call display_int(9,21,user_1d_nlines,5)
            endif
         else if ( item_number .eq. 8 ) then
c           Sample frequency
            call get_ints_with_prompt(int_vec, 1,
     1         'Enter frequency to sample data in file')
            if( int_vec(1) .lt. 1 ) then
               call display_message('Sample frequency must be > 0')
            else
               user_1d_sample_freq = int_vec(1)
               call display_int(10,24,user_1d_sample_freq,3)
            endif
         else if ( item_number .eq. 9 ) then
c           Dataset number
            if( user_1d_dim .eq. 1 ) then
               call get_ints_with_prompt(int_vec, 1,
     1            'Enter dataset number (SPICE output only)')
               if( int_vec(1) .lt. 1 ) then
                  call display_message('Invalid dataset number')
               else
                  user_1d_dataset_number = int_vec(1)
                  call display_int(11,21,user_1d_dataset_number,5)
               endif
            else
               call display_message(
     1                 'Dataset number only for X-Y datafiles')
            endif
         else if ( item_number .eq. 10 ) then
c           Sweep
            if( user_1d_sweep ) then
               user_1d_sweep = .FALSE.
               str = ' no'
            else
               user_1d_sweep = .TRUE.
               str = 'yes'
            endif
            call display_string(12,35,str,3,1)
         else if ( item_number .eq. 11 ) then
            if( have_x_expr ) then
               call display_message(
     1         'Both AX and X expression cannot be specified')
            else
               real_vec(1) = user_1d_ax
               call get_reals_with_prompt(real_vec,1,
     1           'Enter X coordinate offset (AX in formula: MX*x + AX)')
               user_1d_ax = real_vec(1)
               call display_real(13,26,user_1d_ax,1)
            endif
         else if ( item_number .eq. 12 ) then
            if( have_x_expr ) then
               call display_message(
     1         'Both MX and X expression cannot be specified')
            else
               real_vec(1) = user_1d_mx
               call get_reals_with_prompt(real_vec,1,
     1           'Enter X coordinate scale (MX in formula: MX*x + AX)')
               if( real_vec(1) .eq. 0.0 ) then
                  call display_message('MX must not be set to zero')
               else
                  user_1d_mx = real_vec(1)
                  call display_real(14,26,user_1d_mx,1)
               endif
            endif
         else if ( item_number .eq. 13 ) then
            if( have_y_expr ) then
               call display_message(
     1         'Both AY and Y expression cannot be specified')
            else
               real_vec(1) = user_1d_ay
               call get_reals_with_prompt(real_vec,1,
     1           'Enter Y coordinate offset (AY in formula: MY*y + AY)')
               user_1d_ay = real_vec(1)
               call display_real(15,26,user_1d_ay,1)
            endif
         else if ( item_number .eq. 14 ) then
            if( have_y_expr ) then
               call display_message(
     1         'Both MY and Y expression cannot be specified')
            else
               real_vec(1) = user_1d_my
               call get_reals_with_prompt(real_vec,1,
     1           'Enter Y coordinate offset (AY in formula: MY*y + AY)')
               if( real_vec(1) .eq. 0.0 ) then
                  call display_message('MY must not be set to zero')
               else
                  user_1d_my = real_vec(1)
                  call display_real(16,26,user_1d_my,1)
               endif
            endif
         else if ( item_number .eq. 15 ) then
            if( user_1d_dim .eq. 1 .and.
     1          .not. (user_1d_contour .or. user_1d_errorbars) ) then
               call display_message('AZ not applicable in X-Y mode')
            else
               real_vec(1) = user_1d_az
               call get_reals_with_prompt(real_vec,1,
     1           'Enter Z coordinate offset (AZ in formula: MZ*z + AZ)')
               user_1d_az = real_vec(1)
               call display_real(17,26,user_1d_az,1)
            endif
         else if ( item_number .eq. 16 ) then
            if( user_1d_dim .eq. 1 .and.
     1          .not. (user_1d_contour .or. user_1d_errorbars) ) then
               call display_message('MZ not applicable in X-Y mode')
            else
               real_vec(1) = user_1d_mz
               call get_reals_with_prompt(real_vec,1,
     1           'Enter Z coordinate offset (AZ in formula: MZ*z + AZ)')
               if( real_vec(1) .eq. 0.0 ) then
                  call display_message('MZ must not be set to zero')
               else
                  user_1d_mz = real_vec(1)
                  call display_real(18,26,user_1d_mz,1)
               endif
            endif
         else if ( item_number .eq. 17 ) then
c           X column expression
            if( user_1d_dim .eq. 2 ) then
               call display_message(
     1            'X expression not applicable in X-Y-Z mode')
            else
               call get_string_with_prompt(user_1d_x_expression,
     1           'Enter expression to apply to x column data '//
     2           '(e.g. 3.0*x**2 + sqrt(sin(x)) )')
               ilen = string_len(user_1d_x_expression)
               if( ilen .eq. 0 ) then
                  call display_string(19,27,'(none)',52,1)
                  have_x_expr = .FALSE.
                  call display_real(13,26,user_1d_ax,1)
                  call display_real(14,26,user_1d_mx,1)
               else
                  call display_string(19,27,user_1d_x_expression,52,1)
                  call display_string(13,26,'       NA',9,1)
                  call display_string(14,26,'       NA',9,1)
                  have_x_expr = .TRUE.
               endif
            endif
         else if ( item_number .eq. 18 ) then
c           Y column expression
            if( user_1d_dim .eq. 2 ) then
               call display_message(
     1            'Y expression not applicable in X-Y-Z mode')
            else
               call get_string_with_prompt(user_1d_y_expression,
     1              'Enter expression to apply to y column data '//
     2              '(e.g. 3.0*y**2 + sqrt(sin(y)) )')
               ilen = string_len(user_1d_y_expression)
               if( ilen .eq. 0 ) then
                  call display_string(20,27,'(none)',52,1)
                  have_y_expr = .FALSE.
                  call display_real(15,26,user_1d_ay,1)
                  call display_real(16,26,user_1d_my,1)
               else
                  call display_string(20,27,user_1d_y_expression,52,1)
                  call display_string(15,26,'       NA',9,1)
                  call display_string(16,26,'       NA',9,1)
                  have_y_expr = .TRUE.
               endif
            endif
         else if ( item_number .eq. 19 ) then
c           Rectangular grid
            if( user_1d_dim .eq. 1 ) then
               call display_message('Not applicable in X-Y mode')
            else
               if( user_1d_rect_grid ) then
                  user_1d_rect_grid = .FALSE.
                  str = ' no'
               else
                  user_1d_rect_grid = .TRUE.
                  str = 'yes'
               endif
               call display_string(13,71,str,3,1)
            endif
         else if ( item_number .eq. 20 ) then
c           Merge Y column data
            user_1d_merge_ycols = .NOT. user_1d_merge_ycols
            if( user_1d_merge_ycols ) then
               str = 'yes'
            else
               str = ' no'
            endif
            call display_string(14,71,str,3,1)
         else
            call display_message('Item number out of range')
         endif
      endif
      goto 200
c
1000  call display_message('Illegal input')
      goto 200
c
300   if( do_read ) then
         if( user_1d_dim .eq. 1 ) then
            call do_read_asciifile
         else
            if( user_1d_rect_grid ) then
               call do_read_asciifile_2d
            else
               call do_read_asciifile_2d_tri
            endif
         endif
      else
         have_user_1d_data = .FALSE.
      endif
c
      return
      end
c
      subroutine do_read_asciifile
      implicit none
c
      include 'const.h'
      include 'sysdep.cmn'
      include 'plot.cmn'
      include 'data.cmn'
      include 'io.cmn'
      include 'mesh.cmn'
      include 'user.cmn'
      include 'gui.cmn'
      include 'evaluate.cmn'
c
c     read an ASCII data file (1D), using parameters in user.cmn
c
c     locals
c
      double precision tmp(MAXCOLS)
      integer i, j, k, ncols, ioerr, ilen, lineno, linect, itype,
     1        ierr, nrecl, icol, ind, datact, nvars, offset
      integer string_len
      logical input_active, warned, last_line
c     For reading SPICE ASCII output file
      logical is_spice_output, is_hspice_output, lerr
      character*(PM_MAX_PATHLEN) expanded_name
c     Use extra large input buffer for long line data
      character*1000 buffer
c     Variables for parsed (X column character) input
      integer ipos, nchar_data_lines
      character*80  key, xchar_data
      character*8   var_names(MAXCOLS)
c
      integer istat, pm_put_error_msg
c
c     GUI error level: 0=success, 1=warning, 2=error
      gui_error_level = 0
      call pm_clear_error_msg
c
      prev_binfiletype = binfiletype
      binfiletype = DT_ASCII
      binfilename = user_1d_asciifile
      datafile_record = user_1d_dataset_number
c     get stem of file name (for hardcopy)
      call get_filestem(user_1d_asciifile, binfilestem)
c     Initialize x/y name and units
      user_1d_x_name  = ' '
      user_1d_x_units = ' '
      user_1d_y_name  = 'User defined'
      user_1d_y_units = ' '
c
      if( .NOT. lgui ) then
         write(luttyo,10)
10       format(' ','Reading ascii file...',$)
      endif
c
c     If we have to determine the number of Y columns 'on the fly',
c     find out now...
      if( user_1d_col_y(1) .eq. -1 ) then
         call pm_find_all_y_columns(user_1d_asciifile, asciifil,
     1            user_1d_skip, datafile_record, buffer, user_1d_col_x,
     1            user_1d_n_ycols, user_1d_col_y, lineno, MAXCOLS, ierr)
         if( ierr .ne. 0 ) then
            goto 10040
         endif
      endif
      if( (user_1d_n_ycols+4) .gt. MULTI_SIZE) then
c        scaled Y + unscaled X,Y + Z (scaled,unscaled) = 5 slots
c        in arr2d() array...
         goto 10040
      endif
c
      if( user_1d_array_size .eq. 0 ) then
         user_1d_array_size = user_1d_n_ycols*COMP_ARRAYSIZE
         user_1d_data_ptr  = pm_malloc(4*user_1d_array_size)
         user_1d_coord_ptr = pm_malloc(4*user_1d_array_size)
         user_1d_zval_ptr  = pm_malloc(4*user_1d_array_size)
      else if( user_1d_array_size .lt.
     1         user_1d_n_ycols*COMP_ARRAYSIZE ) then
         call pm_free(user_1d_data_ptr)
         call pm_free(user_1d_coord_ptr)
         call pm_free(user_1d_zval_ptr)
         user_1d_array_size = user_1d_n_ycols*COMP_ARRAYSIZE
         user_1d_data_ptr  = pm_malloc(4*user_1d_array_size)
         user_1d_coord_ptr = pm_malloc(4*user_1d_array_size)
         user_1d_zval_ptr  = pm_malloc(4*user_1d_array_size)
      endif
c
      call zero_real_array(user_1d_coord,user_1d_array_size,1)
      call zero_real_array(user_1d_data,user_1d_array_size,1)
      call zero_real_array(user_1d_zval,user_1d_array_size,1)
c
      have_user_1d_data = .FALSE.
      is_spice_output = .FALSE.
      is_hspice_output = .FALSE.
c
      ncols = user_1d_col_x
      do icol=1,user_1d_n_ycols
        ncols = MAX(ncols,user_1d_col_y(icol))
      end do
      if( user_1d_contour .or. user_1d_errorbars )
     1    ncols = MAX(ncols,user_1d_col_z)
c
      if( ncols .gt. MAXCOLS ) goto 10020
c
c     Loop to read input
      warned = .FALSE.
      user_1d_nx = 0
c
      input_active = .FALSE.
c
      nrecl=0
      call pm_open_file(asciifil, user_1d_asciifile,
     1                  'OLD','FORMATTED','READ','LIST','SHARED',
     2                  nrecl, ioerr, ierr)
      if( ierr .ne. 0 ) goto 10000
c
      lineno = 0
      linect = 0
      datact = -1
      nchar_data_lines = 0
c
      do 1000 i=1,user_1d_skip
         read(asciifil,2000,err=10010,iostat=ioerr,end=10010) buffer
         lineno = lineno + 1
1000  continue
c
      input_active = .TRUE.
      last_line = .FALSE.
c
2010  if( last_line ) goto 1500
c
      read(asciifil,2000,err=10010,iostat=ioerr,end=1500) buffer
2000  format(a)
      lineno = lineno + 1
      linect = linect + 1
c
      if( user_1d_nlines .gt. 0 ) then
         if( linect .eq. user_1d_nlines ) last_line = .TRUE.
      endif
c
c     Skip comments lines beginning with: ! c C ; # *
c     Note: files created by FORTRAN may have a blank in column 1
      if( buffer(1:1) .eq. ' ' ) then
         i = 2
      else
         i = 1
      endif
      if( buffer(i:i) .eq. '!' .or. buffer(i:i) .eq. 'c' .or.
     1    buffer(i:i) .eq. 'C' .or. buffer(i:i) .eq. ';' .or.
     2    buffer(i:i) .eq. '#' .or. buffer(i:1) .eq. '*' ) goto 2010
c
c     SPICE output file support for W. Anderson
      if( linect .eq. 1 .and. INDEX(buffer,'SPICE') .ne. 0 ) then
         if( INDEX(buffer,'HSPICE') .ne. 0 ) then
            is_hspice_output = .TRUE.
         endif
         is_spice_output = .TRUE.
         call pm_skip_to_spice_xydata(
     1      asciifil,buffer,lineno,linect,user_1d_dataset_number,
     2      user_1d_col_x, user_1d_col_y(1), is_hspice_output, 
     3      user_1d_x_name, user_1d_x_units,
     4      user_1d_y_name, user_1d_y_units, lerr)
      endif
      if( lerr ) goto 10030
c     End of SPICE .PRINT output is 'Y' in column 1
      if( is_spice_output .and.
     1    (buffer(1:1) .eq. 'Y' .or. buffer(1:1) .eq. 'y') ) goto 1500
c
c     Skip blank lines
      if( string_len(buffer) .eq. 0 ) goto 2010
c
      if( buffer(1:4) .eq. '.OFF' .or. buffer(1:5) .eq. ' .OFF' .or.
     1    buffer(1:4) .eq. '.off' .or. buffer(1:5) .eq. ' .off') then
         input_active = .FALSE.
      else if( buffer(1:3) .eq. '.ON' .or. buffer(1:4) .eq. ' .ON' .or.
     1         buffer(1:3) .eq. '.on' .or. buffer(1:4) .eq. ' .on')then
         input_active = .TRUE.
      else if( input_active ) then
         call detab(buffer)
         call pm_mangle_asciifile_line(buffer)
         if( user_1d_x_char_data ) then
c           Parse each non-blank character string
            ipos = 1
            do k=1,ncols
               call get_keyword(buffer,key,ipos)
               if( ipos .eq. 0 ) then
c                 We fake a FORTRAN IO error and exit
                  ioerr = PM_FOR_EOF_DUR_READ
                  goto 10010
               endif
               if( k .eq. user_1d_col_x ) then
                  nchar_data_lines = nchar_data_lines + 1
                  tmp(user_1d_col_x) = nchar_data_lines
                  xchar_data = key
               else
                  do icol=1,user_1d_n_ycols
                     if( k .eq. user_1d_col_y(icol) ) then
                        read(key,*,end=10010,err=10010,iostat=ioerr)
     1                       tmp(user_1d_col_y(icol))
                     endif
                  end do
               endif
            end do
         else
**          This is an alternative line read that will not barf
**          if one of the columns has character data in it.
**          This is likely to be slower than the original code.
**          Here is the original reader code:
**            read(buffer,*,err=10010,iostat=ioerr,end=10010)
**     1          (tmp(i), i=1,ncols)
c           Parse each non-blank character string
            ipos = 1
            do k=1,ncols
               call get_keyword(buffer,key,ipos)
c              Check missing column
c              If so, branch to read of next line...
c              (the check for t/f is to make sure that the key is not
c              interpreted as true/false)
               if( ipos .eq. 0 .or.
     1             INDEX('ftFT',key(1:1)) .ne. 0) goto 2010
               if( k .eq. user_1d_col_x ) then
                  read(key,*,end=10010,err=10010,iostat=ioerr)
     1                 tmp(user_1d_col_x)
               else
                  if( (user_1d_sort .or. user_1d_contour .or.
     1                 user_1d_errorbars) .and.
     2                (k .eq. user_1d_col_z) ) then
                     read(key,*,end=10010,err=10010,iostat=ioerr)
     1                    tmp(user_1d_col_z)
                  endif
                  do icol=1,user_1d_n_ycols
                     if( k .eq. user_1d_col_y(icol) ) then
                        read(key,*,end=10010,err=10010,iostat=ioerr)
     1                       tmp(user_1d_col_y(icol))
                     endif
                  end do
               endif
            end do
         endif
         datact = datact +  1
         if( user_1d_nx .lt. COMP_ARRAYSIZE .and.
     1       MOD(datact,user_1d_sample_freq) .eq. 0) then
            user_1d_nx = user_1d_nx + 1
            user_1d_coord(user_1d_nx) = tmp(user_1d_col_x)
c           We load each Y column into user_1d_data, using COMP_ARRAYSIZE
c           as an offset...
            do icol=1,user_1d_n_ycols
               user_1d_data((icol-1)*COMP_ARRAYSIZE + user_1d_nx) =
     1            tmp(user_1d_col_y(icol))
            end do
            if( user_1d_contour .or. user_1d_errorbars .or.
     1          user_1d_sort )
     1         user_1d_zval(user_1d_nx)  = tmp(user_1d_col_z)
            if( user_1d_x_char_data ) then
               if( user_1d_nx .gt. MAX_LINES_WITH_CHAR_DATA .and.
     1             (.not. warned)) then
                  write(msgbuf,130) MAX_LINES_WITH_CHAR_DATA
                  istat = pm_put_error_msg(msgbuf)
                  istat = pm_put_error_msg(
     1                    'The remainder have been ignored')
                  warned = .TRUE.
                  gui_error_level = 1
               else
                  user_1d_char_data(user_1d_nx) = xchar_data
               endif
            endif
         else if(user_1d_nx .eq. COMP_ARRAYSIZE .and.
     1          (.not. warned)) then
            write(msgbuf,130) COMP_ARRAYSIZE
130         format('Number of data points exceeds limit of ',i6)
            istat = pm_put_error_msg(msgbuf)
            istat = pm_put_error_msg('The remainder have been ignored')
            warned = .TRUE.
            gui_error_level = 1
         endif
      endif
c
      goto 2010
c
1500  continue
c
      close(unit=asciifil)
c
      if( .not. lgui ) then
         write(luttyo,150)
150      format(' ','done')
      endif
c
c     If user asks to merge all Y column data into one curve,
c     then merge Y column data into user_1d_data()
      if ( user_1d_merge_ycols ) then
         if( user_1d_n_ycols*user_1d_nx .gt. COMP_ARRAYSIZE ) then
            istat = pm_put_error_msg(
     1      'Error: Merged Y column data exceeds internal array size')
            gui_error_level = 2
         else
            offset = user_1d_nx
            do j=2,user_1d_n_ycols
               do i=1,user_1d_nx
                  user_1d_data(i+offset) =
     1               user_1d_data((j-1)*COMP_ARRAYSIZE+i)
                  user_1d_coord(i+offset) = user_1d_coord(i)
               end do
               offset = offset + user_1d_nx
            end do
            user_1d_nx = user_1d_n_ycols*user_1d_nx
         endif
      endif
c
      if( user_1d_nx .eq. 0 ) then
         istat = pm_put_error_msg(
     1           'Error: No data found in file, or incorrect column')
         gui_error_level = 2
      else
         if( user_1d_nlines .gt. 0 .and.
     1       linect .lt. user_1d_nlines ) then
            istat = pm_put_error_msg('Warning: end of file reached')
            istat = pm_put_error_msg(
     1              'before specified number of lines were read')
            gui_error_level = 1
         endif
         have_user_1d_data = .TRUE.
         if( user_1d_x_char_data ) then
            user_1d_nx = MIN(user_1d_nx,MAX_LINES_WITH_CHAR_DATA)
         endif
c
c        Now, copy data to Postmini main storage area
c
c        Clear out arrays
         do 200 j=1,3
            do 200 i=1,COMP_ARRAYSIZE
               xyz(i,j) = 0.0
               edxyz(i,j) = 0.0
               dxyzm(i,j) = 0.0
200      continue
         do 201 j=1,MENUSIZE
            in_binary_file(j) = .FALSE.
201      continue
         call zero_real_array(arr2d,COMP_ARRAYSIZE,MENUSIZE)
c
         do 203 i=1,user_1d_nx
c           Scaled data goes in slot 1
            xyz(i,1)=(user_1d_mx*user_1d_coord(i) + user_1d_ax)*MU2CM
            arr2d(i,1) = user_1d_my*user_1d_data(i) + user_1d_ay
            if( user_1d_xabs ) xyz(i,1)   = ABS(xyz(i,1))
            if( user_1d_yabs ) arr2d(i,1) = ABS(arr2d(i,1))
c           Unscaled data goes in slots 2 and 3 (so we change scaling
c           later without re-reading file)
            arr2d(i,2) = user_1d_coord(i)*MU2CM
            arr2d(i,3) = user_1d_data(i)
c           "Z" data goes in slots 4 and 5 (unscaled, scaled)
            if( user_1d_contour .or. user_1d_errorbars ) then
               arr2d(i,4) = user_1d_zval(i)
               arr2d(i,5) = (user_1d_mz*user_1d_zval(i) + user_1d_az)
               if( user_1d_zabs ) arr2d(i,5) = ABS(arr2d(i,5))
            endif
            if( user_1d_n_ycols .gt. 1 .and.
     1          .not. user_1d_merge_ycols) then
               do icol=2,user_1d_n_ycols
c                 Additional Y columns go in slots 6 and higher
                  arr2d(i,icol+4) =
     1               user_1d_data((icol-1)*COMP_ARRAYSIZE + i)
               end do
            endif
203      continue
c
         if( string_len(user_1d_x_expression) .ne. 0 ) then
c           Apply X expression
            var_names(1) = 'x'
            call ev_expression_f(user_1d_x_expression,var_names,1,
     1                           user_1d_nx,
     2                           user_1d_coord,COMP_ARRAYSIZE,
     3                           xyz(1,1),ierr,ind)
            if( ierr .ne. EVAL_SUCCESS ) goto 10050
            do i=1,user_1d_nx
               xyz(i,1) = xyz(i,1)*MU2CM
            end do
         endif
c
         if( string_len(user_1d_y_expression) .ne. 0 ) then
c           Apply Y expression
            var_names(1) = 'x'
            if( user_1d_n_ycols .eq. 1 ) then
               var_names(2) = 'y'
               nvars = 2
            else
               nvars = 1
               if( user_1d_merge_ycols ) then
                  nvars = 2
                  var_names(nvars) = 'y'
               else
                  do i=1,user_1d_n_ycols
                     nvars = nvars + 1
                     if( user_1d_col_y(i) .lt. 10 ) then
                        write(var_names(i+1),140) user_1d_col_y(i)
140                     format('y',i1)
                     else
                        write(var_names(i+1),141) user_1d_col_y(i)
141                     format('y',i2)
                     endif
                  end do
               endif
            endif
            do i=1,user_1d_nx
               work_vec(i,1) = user_1d_coord(i)
               do j=1,user_1d_n_ycols
                  work_vec(i,j+1) = user_1d_data((j-1)*COMP_ARRAYSIZE+i)
               end do
            end do
            call ev_expression_f(user_1d_y_expression,var_names,nvars,
     1                           user_1d_nx,
     2                           work_vec,COMP_ARRAYSIZE,
     3                           arr2d(1,1),ierr,ind)
            if( ierr .ne. EVAL_SUCCESS ) goto 10060
         endif
c
         nx = user_1d_nx
c        Fake second and third dimension
         ny = 1
         xyz(1,2) = 0.0
c
         nz = 1
         nt = ny
         nyoxm = 0
         nytot= ny
         nxsp = 1
         nxsoxm = 1
         plane_index = 1
         prev_plane_type = plane_type
         plane_type = XY_PLANE
         xyz(1,3) = 0.0
         nxy=nx*ny
         nxytot=nx*nytot
c
c        This variable must be set by all read routines...
         data_is_trigrid = .FALSE.
c
         in_binary_file(1) = .TRUE.
c
c        Load generic mesh pointers, depending on plane type
         call set_3d_index_limits
c
c        Set up boundary limits information
         call coord_limits
c
         nxm = nx - 1
         nym = ny - 1
         nytotm = nytot - 1
c
c        inverse mesh spacing...
c
         do 204 i=1,nxm
            if( (xyz(i+1,1) - xyz(i,1)) .eq. 0.0 ) then
               edxyz(i,1) = PM_MAX_FLOAT
            else
               edxyz(i,1) = 1.0/(xyz(i+1,1) - xyz(i,1))
            endif
204      continue
         do 205 i=1,nytotm
205         edxyz(i,2) = 1.0/(xyz(i+1,2) - xyz(i,2))
         do 206 i=1,nz-1
206         edxyz(i,3) = 1.0/(xyz(i+1,3) - xyz(i,3))
c
c        average mesh spacing...
c
         dxyzm(1,1)  = (xyz(2,1)  - xyz(1,1))/2.0
         dxyzm(nx,1) = (xyz(nx,1) - xyz(nx-1,1))/2.0
         do 207 i=2,nxm
207        dxyzm(i,1) = (xyz(i+1,1) - xyz(i-1,1))/2.0
c
         dxyzm(1,2)     = (xyz(2,2)  - xyz(1,2))/2.0
         dxyzm(nytot,2) = (xyz(nytot,2) - xyz(nytot-1,2))/2.0
         do 208 i=2,nytotm
208         dxyzm(i,2) = (xyz(i+1,2) - xyz(i-1,2))/2.0
c
         dxyzm(1,3)     = (xyz(2,3)  - xyz(1,3))/2.0
         dxyzm(nz,3) = (xyz(nz,3) - xyz(nz-1,3))/2.0
         do 209 i=2,nz-1
209         dxyzm(i,3) = (xyz(i+1,3) - xyz(i-1,3))/2.0
c
         itype = 1
         call load_ascii_specific(itype)
c
         if( lfirst_read ) then
            lfirst_read = .FALSE.
            prev_plane_type = plane_type
            prev_binfilename = binfilename
            prev_binfiletype = binfiletype
         endif
c        Force plot to rescale each time we read an ASCII file
         oned_lfirst = .TRUE.
         twod_lfirst = .TRUE.
         threed_lfirst = .TRUE.
c
         datafile_loaded = .TRUE.
c
      endif
c
      goto 20000
c
c     errors of commission/omission
c
c     I/O error on open of ascii file
10000 continue
      call expand_filename(user_1d_asciifile,expanded_name)
      ilen = string_len(expanded_name)
      write(msgbuf,10001) ioerr
10001 format('FORTRAN I/O error #',i5,' on open of ascii file:')
      istat = pm_put_error_msg(msgbuf)
      write(msgbuf,10002) expanded_name(1:ilen)
10002 format(a)
      istat = pm_put_error_msg(msgbuf)
      istat = pm_put_error_msg('Check for these errors:')
      istat = pm_put_error_msg(
     1     '   - Incorrect file, directory or disk name')
      istat = pm_put_error_msg(
     1     '   - You must have read permission on the file')
      gui_error_level = 2
c     Go back....
      goto 20000
c
c
c     I/O error on read of ascii file
10010 continue
c     Check for 'innocuous' file read errors.
c     If so, go back and read next line.
      if( input_active .and.
     1    (ioerr .eq. -1 .or.
     2     ioerr .eq. PM_FOR_EOF_DUR_READ .or.
     3     ioerr .eq. PM_FOR_INPUT_CONV_ERR .or.
     4     ioerr .eq. PM_FOR_LIST_IO_SYNTAX)   ) then
         goto 2010
      endif
c
c
      write(msgbuf,10011) ioerr, lineno
10011 format('FORTRAN I/O error #',i5,' on read of ascii file',
     1            ' at line ',i6)
      istat = pm_put_error_msg(msgbuf)
      if( ioerr .eq. PM_FOR_EOF_DUR_READ ) then
c        End-of-file during read
         write(msgbuf,10012)
10012    format('Probable cause: not enough data on line')
         istat = pm_put_error_msg(msgbuf)
      else if( ioerr .eq. -1 ) then       ! End-of-file
         write(msgbuf,10013)
10013    format('Unexpected end-of-file during read')
         istat = pm_put_error_msg(msgbuf)
      else if( ioerr .eq. PM_FOR_INPUT_CONV_ERR ) then
c        Input conversion error
         write(msgbuf,10014)
10014    format('Data in column is not a number')
         istat = pm_put_error_msg(msgbuf)
      endif
      close(unit=asciifil)
      gui_error_level = 2
      goto 20000
c
10020 write(msgbuf,10021) MAXCOLS
10021 format('Error: too many columns specified. Only ',i5,
     1            'supported')
      istat = pm_put_error_msg(msgbuf)
      gui_error_level = 2
      goto 20000
c
10030 write(msgbuf,10031) lineno
10031 format('Error: during read of SPICE output file at line: ',i6)
      istat = pm_put_error_msg(msgbuf)
      close(unit=asciifil)
      gui_error_level = 2
      goto 20000
c
10040 write(msgbuf,10041) lineno
10041 format('Error: while determining number of Y columns at line: ',
     1       i6)
      istat = pm_put_error_msg(msgbuf)
      close(unit=asciifil)
      gui_error_level = 2
      goto 20000
c
10050 write(msgbuf,10051)
10051 format('Error: while evaluating X column expression')
      istat = pm_put_error_msg(msgbuf)
      call ev_lookup_error_message(ierr,msgbuf)
      istat = pm_put_error_msg(msgbuf)
      if( ind .gt. 0 ) then
         istat = pm_put_error_msg(user_1d_x_expression)
         msgbuf = ' '
         msgbuf(ind:ind) = '^'
         istat = pm_put_error_msg(msgbuf)
      endif
      have_user_1d_data = .FALSE.
      gui_error_level = 2
      goto 20000
c
10060 write(msgbuf,10061)
10061 format('Error: while evaluating Y column expression')
      istat = pm_put_error_msg(msgbuf)
      call ev_lookup_error_message(ierr,msgbuf)
      istat = pm_put_error_msg(msgbuf)
      if( ind .gt. 0 ) then
         istat = pm_put_error_msg(user_1d_y_expression)
         msgbuf = ' '
         msgbuf(ind:ind) = '^'
         istat = pm_put_error_msg(msgbuf)
      endif
      have_user_1d_data = .FALSE.
      gui_error_level = 2
      goto 20000
c
20000 continue
      return
      end
c
      subroutine load_ascii_specific(itype)
      implicit none
c
c     Load descriptive arrays with ASCII specific data
c
      include 'const.h'
      include 'sysdep.cmn'
      include 'data.cmn'
      include 'user.cmn'
c
      integer itype   ! 1== 1-D, 2==2-D data
c
c     Data varies linear or logarithmic
      logical LOG, LIN
      parameter (LOG = .TRUE., LIN = .FALSE.)
c     Data has value in oxide, or not
      logical OX, NO_OX
      parameter (OX = .TRUE., NO_OX = .FALSE.)
c     Data jumps at oxide interfaces
      logical JUMP, NO_JUMP
      parameter (JUMP = .TRUE., NO_JUMP = .FALSE.)
c     Data is 1D or 2D
      logical ONED, TWOD
      parameter (ONED = .TRUE., TWOD = .FALSE.)
c
c     Number of menu items
      max_menu_num = 1
c     Array index of net doping
      net_dop_array_index = 0
c
      if( itype .eq. 1 ) then
         call load_desc_arr(1, user_1d_y_name, 'ASCII',
     1                      user_1d_y_units,' ',' ','.dat',
     2                      LIN,NO_OX,NO_JUMP,ONED,1)
      else
         call load_desc_arr(1, 'User defined', 'ASCII',
     1                      ' ',' ',' ','.dat',
     2                      LIN,NO_OX,NO_JUMP,TWOD,1)
      endif
c
      return
      end
c
      subroutine do_read_asciifile_2d
      implicit none
c
      include 'const.h'
      include 'sysdep.cmn'
      include 'plot.cmn'
      include 'data.cmn'
      include 'io.cmn'
      include 'mesh.cmn'
      include 'user.cmn'
c
c     read an ASCII data file (2D), using parameters in user.cmn
c
c     locals
c
      double precision tmp(MAXCOLS)
      double precision xold, yold, xnew, ynew
      real xtmp, ytmp
      integer i, j, ncols, ioerr, ilen, lineno, linect, itype,
     1        itot, nxprev, ierr, nrecl
      integer string_len
      logical input_active, warned, lfound, lstart, last_line
      character*(PM_MAX_PATHLEN) expanded_name
c     Use extra large input buffer for long line data
      character*1000 buffer
c
      have_user_1d_data = .FALSE.
      prev_binfiletype = binfiletype
      binfiletype = DT_ASCII
      binfilename = user_1d_asciifile
      datafile_record = 1
c     Initialize x/y name and units
      user_1d_x_name  = ' '
      user_1d_x_units = ' '
      user_1d_y_name  = 'User defined'
      user_1d_y_units = ' '
c
      nrecl=0
      call pm_open_file(asciifil, user_1d_asciifile,
     1                  'OLD','FORMATTED','READ','LIST','SHARED',
     2                  nrecl, ioerr, ierr)
      if( ierr .ne. 0 ) goto 10000
c
c     get stem of file name (for hardcopy)
      call get_filestem(user_1d_asciifile, binfilestem)
c
      ncols = MAX(user_1d_col_x,user_1d_col_y(1),user_1d_col_z)
c
      if( ncols .gt. MAXCOLS ) goto 10020
c
      write(luttyo,10)
10    format(' ','Reading ascii file...',$)
c
c     clear out arrays
      do 45 j=1,3
         do 45 i=1,COMP_ARRAYSIZE
            xyz(i,j) = 0.0
            edxyz(i,j) = 0.0
            dxyzm(i,j) = 0.0
45    continue
      do 51 j=1,MENUSIZE
         in_binary_file(j) = .FALSE.
51       continue
c
c     Loop to read input
      warned = .FALSE.
      lstart = .TRUE.
      lfound = .FALSE.
      lineno = 0
      linect = 0
c
      input_active = .FALSE.
c
      do 1000 i=1,user_1d_skip
         read(asciifil,2000,err=10010,iostat=ioerr,end=10010) buffer
         lineno = lineno + 1
1000  continue
c
      input_active = .TRUE.
      last_line = .FALSE.
c
2010  if( last_line ) goto 1500
c
      read(asciifil,2000,err=10010,iostat=ioerr,end=1500) buffer
2000  format(a)
      lineno = lineno + 1
      linect = linect + 1
c
      if( user_1d_nlines .gt. 0 ) then
         if( linect .eq. user_1d_nlines ) last_line = .TRUE.
      endif
c
c     Skip comments lines beginning with: ! C ; #
c     Files created by FORTRAN may have a blank in column 1
      call upcase(buffer)
      if( buffer(1:1) .eq. '!' .or. buffer(1:2) .eq. ' !' .or.
     1    buffer(1:1) .eq. 'C' .or. buffer(1:2) .eq. ' C' .or.
     2    buffer(1:1) .eq. ';' .or. buffer(1:2) .eq. ' ;' .or.
     3    buffer(1:1) .eq. '#' .or. buffer(1:2) .eq. ' #' ) goto 2010
c
c     Skip blank lines
      if( string_len(buffer) .eq. 0 ) goto 2010
c
      if( buffer(1:4) .eq. '.OFF' .or. buffer(1:5) .eq. ' .OFF' ) then
         input_active = .FALSE.
      else if( buffer(1:3) .eq. '.ON' .or. buffer(1:4) .eq. ' .ON')then
         input_active = .TRUE.
      else if( input_active ) then
         call detab(buffer)
         read(buffer,*,err=10010,iostat=ioerr,end=10010)
     1       (tmp(i), i=1,ncols)
         xnew = tmp(user_1d_col_x)
         ynew = tmp(user_1d_col_y(1))
         if( lstart ) then
            lstart = .FALSE.
            nxprev = 0
            nx = 1
            ny = 1
            itot = 1
         else
            if( xnew .lt. xold ) then
c              Assume we are starting a new y value
               if( ynew .le. yold ) then
c                 Error
                  goto 10030
               else
                  ny = ny + 1
                  itot = itot + 1
c                 Check to make sure each row has same number of points
                  if( nxprev .ne. nx .and. nxprev .ne. 0 ) then
c                    Error
                     goto 10040
                  else
                     nxprev = nx
                     nx = 1
                  endif
               endif
            else if( xnew .eq. xold ) then
c              Error
               goto 10050
            else
               nx = nx + 1
               itot = itot + 1
            endif
         endif
         if( nx .GT. COMP_ARRAYSIZE .or. ny .GT. COMP_ARRAYSIZE .or.
     1       itot .GT. WORK2DSIZE ) then
            if( .NOT. warned ) then
               if( nx .GT. COMP_ARRAYSIZE )
     1            write(luttyo,130) 'X', COMP_ARRAYSIZE
               if( ny .GT. COMP_ARRAYSIZE )
     1             write(luttyo,130) 'Y', COMP_ARRAYSIZE
               if( itot .GT. WORK2DSIZE )
     1             write(luttyo,130) 'total', WORK2DSIZE
130            format(' ','Error: The number of ',a,
     1                    ' data points exceeds limit of ',i6)
               warned = .TRUE.
            endif
         else
            xold = xnew
            yold = ynew
            xtmp = (user_1d_mx*tmp(user_1d_col_x) + user_1d_ax)*MU2CM
c           Check to make sure x mesh is consistent from row to row
            if( ny .gt. 1 ) then
               if( xyz(nx,1) .ne. xtmp ) then
c                 Error
                  goto 10060
               endif
            else
               xyz(nx,1) = xtmp
            endif
            ytmp = MU2CM*(user_1d_my*tmp(user_1d_col_y(1)) + user_1d_ay)
            if( nx .gt. 1 ) then
               if( xyz(ny,2) .ne. ytmp ) then
c                 Error
                  goto 10070
               endif
            else
               xyz(ny,2) = ytmp
            endif
            work(itot) = user_1d_mz*tmp(user_1d_col_z) + user_1d_az
            lfound = .TRUE.
         endif
      endif
c
      goto 2010
c
1500  continue
      write(luttyo,150)
150   format(' ','done')
      close(unit=asciifil)
c
      if( .NOT. lfound ) then
         write(luttyo,151)
151      format(' ','Error: No data found in file, or incorrect column')
      else if ( nx*ny .ne. itot ) then
         write(luttyo,152)
152      format(' ','Error: nx*ny not equal to number of data points')
c     Check to make sure each row has same number of points
      else if( nxprev .ne. nx .and. nxprev .ne. 0 ) then
         write(luttyo,153)
153      format(' ',
     1   'Error: number of x points is not the same for all rows')
      else
         have_user_1d_data = .TRUE.
c
c        Now, copy data to Postmini main storage area
c
         call allocate_rect_arrays(nx,ny,1,ierr)
         if( ierr .ne. 0 ) then
c           Failure during array allocation
            write(luttyo,10080)
10080       format(' ','Internal error: could not allocate data array')
            have_user_1d_data = .FALSE.
            return
         endif
c        Zero array (not really necessary)
         call zero_real_array(arrdat,nx*ny,1)
c
c        Copy data to the arrdat array
         do i=1,nx*ny
            arrdat(i) = work(i)
         end do
c
         nz = 1
         nt = ny
         nyoxm = 0
         nytot= ny
         nxsp = 1
         nxsoxm = 1
         plane_index = 1
         prev_plane_type = plane_type
         plane_type = XY_PLANE
         xyz(1,3) = 0.0
         nxy=nx*ny
         nxytot=nx*nytot
c
c        This variable must be set by all read routines...
         data_is_trigrid = .FALSE.
c
         in_binary_file(1) = .TRUE.
c
c        Load generic mesh pointers, depending on plane type
         call set_3d_index_limits
c
c        Set up boundary limits information
         call coord_limits
c
         nxm = nx - 1
         nym = ny - 1
         nytotm = nytot - 1
c
c        inverse mesh spacing...
c
         do 204 i=1,nxm
            if( (xyz(i+1,1) - xyz(i,1)) .eq. 0.0 ) then
               edxyz(i,1) = PM_MAX_FLOAT
            else
               edxyz(i,1) = 1.0/(xyz(i+1,1) - xyz(i,1))
            endif
204      continue
         do 205 i=1,nytotm
205         edxyz(i,2) = 1.0/(xyz(i+1,2) - xyz(i,2))
         do 206 i=1,nz-1
206         edxyz(i,3) = 1.0/(xyz(i+1,3) - xyz(i,3))
c
c        average mesh spacing...
c
         dxyzm(1,1)  = (xyz(2,1)  - xyz(1,1))/2.0
         dxyzm(nx,1) = (xyz(nx,1) - xyz(nx-1,1))/2.0
         do 207 i=2,nxm
207        dxyzm(i,1) = (xyz(i+1,1) - xyz(i-1,1))/2.0
c
         dxyzm(1,2)     = (xyz(2,2)  - xyz(1,2))/2.0
         dxyzm(nytot,2) = (xyz(nytot,2) - xyz(nytot-1,2))/2.0
         do 208 i=2,nytotm
208         dxyzm(i,2) = (xyz(i+1,2) - xyz(i-1,2))/2.0
c
         dxyzm(1,3)     = (xyz(2,3)  - xyz(1,3))/2.0
         dxyzm(nz,3) = (xyz(nz,3) - xyz(nz-1,3))/2.0
         do 209 i=2,nz-1
209         dxyzm(i,3) = (xyz(i+1,3) - xyz(i-1,3))/2.0
c
         itype = 2
         call load_ascii_specific(itype)
c
         if( lfirst_read ) then
            lfirst_read = .FALSE.
            prev_plane_type = plane_type
            prev_binfilename = binfilename
            prev_binfiletype = binfiletype
         endif
c        Force plot to rescale each time we read an ASCII file
         oned_lfirst = .TRUE.
         twod_lfirst = .TRUE.
         threed_lfirst = .TRUE.
c
         datafile_loaded = .TRUE.
c
      endif
c
      goto 20000
c
c     errors of commission/omission
c
c     I/O error on open of ascii file
10000 continue
      call expand_filename(user_1d_asciifile,expanded_name)
      ilen = string_len(expanded_name)
      write(luttyo,10001) ioerr, expanded_name(1:ilen)
10001 format(/' ','FORTRAN I/O error #',i5,' on open of ascii file:'/
     1       ' ',a)
      write(luttyo,10002)
10002 format(' ','Check for these errors:'/
     1       ' ','   - Incorrect file, directory or disk name'/
     1       ' ','   - You must have read permission on the file'/)
c     Go back and get new name...
      goto 20000
c
c
c     I/O error on read of ascii file
10010 continue
c     Check for 'innocuous' file read errors.
c     If so, go back and read next line.
      if( input_active .and.
     1    (ioerr .eq. -1 .or.
     2     ioerr .eq. PM_FOR_EOF_DUR_READ .or.
     3     ioerr .eq. PM_FOR_INPUT_CONV_ERR .or.
     4     ioerr .eq. PM_FOR_LIST_IO_SYNTAX)   ) then
         goto 2010
      endif
c
      write(luttyo,10011) ioerr, lineno
10011 format(/' ','FORTRAN I/O error #',i5,' on read of ascii file',
     1            ' at line ',i6)
      if( ioerr .eq. PM_FOR_EOF_DUR_READ ) then
c        End-of-file during read
         write(luttyo,10012)
10012    format(' ','Probable cause: not enough data on line'/)
      else if( ioerr .eq. -1 ) then       ! End-of-file
         write(luttyo,10013)
10013    format(' ','Unexpected end-of-file during read...'/)
      else if( ioerr .eq. PM_FOR_INPUT_CONV_ERR ) then
c        Input conversion error
         write(luttyo,10014)
10014    format(' ','Data in column is not a number'/)
      endif
      close(unit=asciifil)
      goto 20000
c
10020 write(luttyo,10021) MAXCOLS
10021 format(/' ','Error: too many columns specified. Only ',i5,
     1            'supported')
      goto 20000
c
10030 write(luttyo,10031) lineno
10031 format(/' ','Error at line ',i6,' y value is not increasing')
      close(unit=asciifil)
      goto 20000
c
10040 write(luttyo,10041) lineno
10041 format(/' ','Error at line ',i6,': number of x points',
     1            ' is not consistent from row to row')
      close(unit=asciifil)
      goto 20000
c
10050 write(luttyo,10051) lineno
10051 format(/' ','Error at line ',i6,': x values are not increasing')
      close(unit=asciifil)
      goto 20000
c
10060 write(luttyo,10061) lineno
10061 format(/' ','Error at line ',i6,': x values are not consistent',
     1            ' from row to row')
      close(unit=asciifil)
      goto 20000
c
10070 write(luttyo,10071) lineno
10071 format(/' ','Error at line ',i6,': y values are not equal',
     1            ' along a row')
      close(unit=asciifil)
      goto 20000
c
20000 continue
      return
      end
c
      subroutine preview_ascii_file(filename,num_skip,dataset_number,
     1                              read_error)
      implicit none
c
      include 'sysdep.cmn'
      include 'io.cmn'
c
      character*(*) filename
      integer num_skip, dataset_number
      logical read_error
c
      integer i, ilen, ioerr, ierr, nrecl, lineno, linect,
     1        string_len, ixcol, iycol
      logical is_spice_file, is_hspice_file, lerr
      character*1   ch
      character*250 string, spice_header
      character*64  xname, yname, xunits, yunits
c
      integer NLINES
      parameter (NLINES = 15)
c
      call clear_screen
      read_error = .FALSE.
      is_spice_file = .FALSE.
      is_hspice_file = .FALSE.
c
      nrecl=0
      call pm_open_file(asciifil, filename,
     1                  'OLD','FORMATTED','READ','LIST','SHARED',
     2                  nrecl, ioerr, ierr)
      if( ierr .ne. 0 ) goto 10000
c
c     Skip over first num_skip lines in the file
      if( num_skip .ne. 0 ) then
         write(luttyo,1004) num_skip
1004     format(' ','Skipping ',i6,' lines in file...')
         do i=1,num_skip
            read(asciifil,1001,end=10010,err=10010) string
         end do
      endif
c
      lineno = 1
      linect = 1
      read(asciifil,1001,end=10010,err=10010) string
      if( INDEX(string,'SPICE') .ne. 0 ) then
         is_spice_file = .TRUE.
         if( INDEX(string,'HSPICE') .ne. 0 ) then
            is_hspice_file = .TRUE.
         endif
c        Dummy values for ixcol, iycol
         ixcol = 1
         iycol = 2
         call pm_skip_to_spice_xydata(
     1      asciifil,string,lineno,linect,dataset_number,
     2      ixcol, iycol, is_hspice_file,
     3      xname, xunits, yname, yunits, lerr)
         if( lerr ) goto 10010
c        Back up to pick up SPICE header
         if( is_hspice_file ) then
            backspace(asciifil)
            backspace(asciifil)
            backspace(asciifil)
            backspace(asciifil)
            backspace(asciifil)
            backspace(asciifil)
            backspace(asciifil)
            read(asciifil,1001,end=10010,err=10010) spice_header
            read(asciifil,1001,end=10010,err=10010)
            read(asciifil,1001,end=10010,err=10010)
            read(asciifil,1001,end=10010,err=10010)
            read(asciifil,1001,end=10010,err=10010)
            read(asciifil,1001,end=10010,err=10010)
         else
            backspace(asciifil)
            backspace(asciifil)
            backspace(asciifil)
            read(asciifil,1001,end=10010,err=10010) spice_header
            read(asciifil,1001,end=10010,err=10010)
            read(asciifil,1001,end=10010,err=10010)
         endif
      else
         backspace(asciifil)
      endif
c
      if( is_spice_file ) then
         ilen = string_len(spice_header)
         write(luttyo,950) spice_header(1:ilen)
950      format(' ','This is a SPICE output file, with header:'/
     1          ' ',a)
      endif
      write(luttyo,1000) NLINES
1000  format(' ','First ',i5,' lines of file are:')
      do 100 i=1,NLINES
         read(asciifil,1001,end=1003,err=10010) string
1001     format(a)
         ilen = string_len(string)
         write(luttyo,1002) string(1:ilen)
1002     format(' ',a)
100   continue
c
1003  close(unit=asciifil)
      call get_char(ch,'Hit return to continue...',0)
      goto 20000
c
c     Error during open
10000 continue
      read_error = .TRUE.
      goto 20000
c
c     Error during read
10010 continue
      close(unit=asciifil)
      read_error = .TRUE.
      goto 20000
c
20000 continue
c
      return
      end
c
      subroutine detab(buffer)
      implicit none
c
c     Converts TAB characters to spaces in input file
c
      character*(*) buffer
c
      integer i
      character*1 TAB
c
      TAB = CHAR(9)
      do i=1,LEN(buffer)
         if( buffer(i:i) .eq. TAB ) buffer(i:i) = ' '
      end do
c
      return
      end
c
      subroutine pm_skip_to_spice_xydata(
     1           asciifil,buffer,lineno,linect,dataset_number,
     2           ixcol, iycol, is_hspice_file,
     3           xname, xunits, yname, yunits, lerr)
      implicit none
c
      integer asciifil, lineno, linect, dataset_number,
     1        ixcol, iycol
      character*(*) buffer, xname, xunits, yname, yunits
      logical is_hspice_file, lerr
c
      integer irecord, i, ipos
      character*64  key
      character*255 prev_line
c
      lerr = .FALSE.
      irecord = 0
      buffer  = ' '
c
      do while(.TRUE.)
         prev_line = buffer
         read(asciifil,100,end=400,err=400) buffer
100      format(a)
         lineno = lineno + 1
         linect = linect + 1
c        Check for start of X-Y data
         if( buffer(1:4) .eq. 'X   ' .or.
     1       buffer(1:4) .eq. 'x   ' ) then
            irecord = irecord + 1
c           Read next line into buffer and then return...
            if( is_hspice_file ) then
               read(asciifil,100,end=400,err=400) buffer
               read(asciifil,100,end=400,err=400) prev_line
               read(asciifil,100,end=400,err=400) buffer
               read(asciifil,100,end=400,err=400) buffer
            else
               read(asciifil,100,end=400,err=400) buffer
            endif
            if( irecord .eq. dataset_number ) goto 300
         endif
      end do
c
300   continue
c     Now process previous line to extract data names
      ipos = 1
      do i=1,MAX(ixcol,iycol)
         call get_keyword_2(prev_line,' ',key,ipos)
         if( ipos .ne. 0 ) then
            if( i .eq. ixcol ) xname = key
            if( i .eq. iycol ) yname = key
         endif
      end do
c     Now try to guess units
      if( xname(1:1) .eq. 'T' ) then
         xunits = 's'  ! Time
      else if( xname(1:1) .eq. 'V' ) then
         xunits = 'V'  ! Voltage
      else if( xname(1:1) .eq. 'I' ) then
         xunits = 'A'  ! Current
      else if( xname(1:1) .eq. 'C' ) then
         xunits = 'F'  ! Capacitance
      else if( xname(1:1) .eq. 'R' ) then
         xunits = 'Ohm'  ! Resistor
      else if( xname(1:4) .eq. 'FREQ' ) then
         xunits = 'Hz'  ! Frequency (ac analysis)
      else
         xunits = ' '
      endif
      if( yname(1:1) .eq. 'T' ) then
         yunits = 's'  ! Time
      else if( yname(1:1) .eq. 'V' ) then
         yunits = 'V'  ! Voltage
      else if( yname(1:1) .eq. 'I' ) then
         yunits = 'A'  ! Current
      else if( yname(1:1) .eq. 'C' ) then
         yunits = 'F'  ! Capacitance
      else if( yname(1:1) .eq. 'R' ) then
         yunits = 'Ohm'  ! Resistor
      else if( yname(1:4) .eq. 'FREQ' ) then
         yunits = 'Hz'  ! Frequency (ac analysis)
      else
         yunits = ' '
      endif
      goto 500
c
400   continue
c     Error condition
      lerr = .TRUE.
c
500   continue
c
      return
      end

c
      subroutine do_read_asciifile_2d_tri
      implicit none
c
      include 'const.h'
      include 'sysdep.cmn'
      include 'plot.cmn'
      include 'data.cmn'
      include 'io.cmn'
      include 'mesh.cmn'
      include 'user.cmn'
c
c     read an ASCII data file (2D), on an unstructured mesh,
c     using parameters in user.cmn
c
c     locals
c
      double precision tmp(MAXCOLS)
      integer i, j, ncols, ioerr, ilen, lineno, linect, itype,
     1        ierr, nrecl
      integer string_len, pm_delaunay
      logical input_active, warned, lfound, lstart, linit, last_line
      character*(PM_MAX_PATHLEN) expanded_name
c     Use extra large input buffer for long line data
      character*1000 buffer
c
      have_user_1d_data = .FALSE.
      prev_binfiletype = binfiletype
      binfiletype = DT_ASCII
      binfilename = user_1d_asciifile
      datafile_record = 1
c     Initialize x/y name and units
      user_1d_x_name  = ' '
      user_1d_x_units = ' '
      user_1d_y_name  = 'User defined'
      user_1d_y_units = ' '
c
      nrecl=0
      call pm_open_file(asciifil, user_1d_asciifile,
     1                  'OLD','FORMATTED','READ','LIST','SHARED',
     2                  nrecl, ioerr, ierr)
      if( ierr .ne. 0 ) goto 10000
c
c     get stem of file name (for hardcopy)
      call get_filestem(user_1d_asciifile, binfilestem)
c
      ncols = MAX(user_1d_col_x,user_1d_col_y(1),user_1d_col_z)
c
      if( ncols .gt. MAXCOLS ) goto 10020
c
      write(luttyo,10)
10    format(' ','Reading ascii file...')
c
c     Split up data storage into chunks
      call allocate_tri_arrays(ARR2DSIZE,1,ierr)
      if( ierr .ne. 0 ) then
c        Failure during array allocation
         write(luttyo,10041)
         have_user_1d_data = .FALSE.
         close(unit=binfil)
         return
      endif
c
c     Clear out arrays. Not really necessary.
      call zero_real_array(arrdat,ARR2DSIZE,1)
c
      do i=1,MAX_REGIONS
         imat(i) = 0
      end do
      do i=1,MAX_TRI
         do j=1,3
           nop(j,i) = 0
         end do
      end do
c
      do i=1,ARR2DSIZE
        tri_matpt(i) = -1
      end do
c
      tri_npts  = 0
      tri_ne    = 0
      tri_nreg  = 1
      tri_ndata = 1
      imat(1)   = 1
c
      do j=1,MENUSIZE
         in_binary_file(j) = .FALSE.
      end do
c
c     Loop to read input
      warned = .FALSE.
      lstart = .TRUE.
      lfound = .FALSE.
      lineno = 0
      linect = 0
c
      input_active = .FALSE.
c
      do 1000 i=1,user_1d_skip
         read(asciifil,2000,err=10010,iostat=ioerr,end=10010) buffer
         lineno = lineno + 1
1000  continue
c
      input_active = .TRUE.
      last_line = .FALSE.
c
2010  if( last_line ) goto 1500
c
      read(asciifil,2000,err=10010,iostat=ioerr,end=1500) buffer
2000  format(a)
      lineno = lineno + 1
      linect = linect + 1
c
      if( user_1d_nlines .gt. 0 ) then
         if( linect .eq. user_1d_nlines ) last_line = .TRUE.
      endif
c
c     Skip comments lines beginning with: ! C ; #
c     Files created by FORTRAN may have a blank in column 1
      call upcase(buffer)
      if( buffer(1:1) .eq. '!' .or. buffer(1:2) .eq. ' !' .or.
     1    buffer(1:1) .eq. 'C' .or. buffer(1:2) .eq. ' C' .or.
     2    buffer(1:1) .eq. ';' .or. buffer(1:2) .eq. ' ;' .or.
     3    buffer(1:1) .eq. '#' .or. buffer(1:2) .eq. ' #' ) goto 2010
c
c     Skip blank lines
      if( string_len(buffer) .eq. 0 ) goto 2010
c
      if( buffer(1:4) .eq. '.OFF' .or. buffer(1:5) .eq. ' .OFF' ) then
         input_active = .FALSE.
      else if( buffer(1:3) .eq. '.ON' .or. buffer(1:4) .eq. ' .ON')then
         input_active = .TRUE.
      else if( input_active ) then
         call detab(buffer)
         read(buffer,*,err=10010,iostat=ioerr,end=10010)
     1       (tmp(i), i=1,ncols)
         tri_npts=tri_npts+1
c        Check for point overflow
         if( tri_npts .gt. ARR2DSIZE ) goto 10040
         cord(1,tri_npts) = MU2CM*(user_1d_mx*tmp(user_1d_col_x) +
     1                             user_1d_ax)
         cord(2,tri_npts) = MU2CM*(user_1d_my*tmp(user_1d_col_y(1)) +
     1                             user_1d_ay)
         tri_matpt(tri_npts) = 1
         arrdat(tri_npts) = user_1d_mz*tmp(user_1d_col_z) + user_1d_az
         lfound = .TRUE.
      endif
c
      goto 2010
c
1500  continue
      write(luttyo,150)
150   format(' ','done')
      close(unit=asciifil)
c
      if( .NOT. lfound ) then
         write(luttyo,151)
151      format(' ','Error: No data found in file, or incorrect column')
         have_user_1d_data = .FALSE.
         return
      else
         have_user_1d_data = .TRUE.
      endif
c
c     Triangulate data
      tri_ne = pm_delaunay(cord, nop, tri_npts, MAX_TRI)
      if( tri_ne .lt. 0 ) goto 10080
      do i=1,tri_ne
         tri2reg(i) = 1
      end do
c
c     Compute adjacency information
      linit = .TRUE.
      write(luttyo,*) 'Computing adjacency information...'
      call nextel(nop,tri_nextel,tri_ne,MAX_TRI,linit)
c
c     Set placeholder values for old grid info (probably not needed)
      nx = 1
      ny = 1
      nt = ny
      nyoxm = 0
      nytot= ny
      nxsp = 1
      nxsoxm = 1
      plane_index = 1
      prev_plane_type = plane_type
      plane_type = XY_PLANE
      xyz(1,3) = -xw/2.0
      nxy=nx*ny
      nxytot=nx*nytot
c
c     Load generic mesh pointers, depending on plane type
c
      call set_3d_index_limits
      cut_plane_axis = charb
c     This variable must be set by all read routines...
      data_is_trigrid = .TRUE.
c     Set up boundary limits information
      call coord_limits
c
      in_binary_file(1) = .TRUE.
c
      itype = 2
      call load_ascii_specific(itype)
c
      if( lfirst_read ) then
         lfirst_read = .FALSE.
         prev_plane_type = plane_type
         prev_binfilename = binfilename
         prev_binfiletype = binfiletype
      endif
c     Force plot to rescale each time we read an ASCII file
      oned_lfirst = .TRUE.
      twod_lfirst = .TRUE.
      threed_lfirst = .TRUE.
c
      datafile_loaded = .TRUE.
c
      goto 20000
c
c     errors of commission/omission
c
c     I/O error on open of ascii file
10000 continue
      call expand_filename(user_1d_asciifile,expanded_name)
      ilen = string_len(expanded_name)
      write(luttyo,10001) ioerr, expanded_name(1:ilen)
10001 format(/' ','FORTRAN I/O error #',i5,' on open of ascii file:'/
     1       ' ',a)
      write(luttyo,10002)
10002 format(' ','Check for these errors:'/
     1       ' ','   - Incorrect file, directory or disk name'/
     1       ' ','   - You must have read permission on the file'/)
c     Go back and get new name...
      goto 20000
c
c     I/O error on read of ascii file
10010 continue
c     Check for 'innocuous' file read errors.
c     If so, go back and read next line.
      if( input_active .and.
     1    (ioerr .eq. -1 .or.
     2     ioerr .eq. PM_FOR_EOF_DUR_READ .or.
     3     ioerr .eq. PM_FOR_INPUT_CONV_ERR .or.
     4     ioerr .eq. PM_FOR_LIST_IO_SYNTAX)   ) then
         goto 2010
      endif
c
      write(luttyo,10011) ioerr, lineno
10011 format(/' ','FORTRAN I/O error #',i5,' on read of ascii file',
     1            ' at line ',i6)
      if( ioerr .eq. PM_FOR_EOF_DUR_READ ) then
c        End-of-file during read
         write(luttyo,10012)
10012    format(' ','Probable cause: not enough data on line'/)
      else if( ioerr .eq. -1 ) then       ! End-of-file
         write(luttyo,10013)
10013    format(' ','Unexpected end-of-file during read...'/)
      else if( ioerr .eq. PM_FOR_INPUT_CONV_ERR ) then
c        Input conversion error
         write(luttyo,10014)
10014    format(' ','Data in column is not a number'/)
      endif
      close(unit=asciifil)
      goto 20000
c
10020 write(luttyo,10021) MAXCOLS
10021 format(/' ','Error: too many columns specified. Only ',i5,
     1            'supported')
      goto 20000
c
10040 continue
      write(luttyo,10041)
10041 format(' ','Error: Number of points exceeds internal storage')
      have_user_1d_data = .FALSE.
      close(unit=binfil)
      goto 20000
c
10080 continue
      write(luttyo,10081)
10081 format(' ','Error: Number of triangles exceeds internal storage')
      have_user_1d_data = .FALSE.
      close(unit=binfil)
      goto 20000
c
20000 continue
      return
      end
c
      subroutine parse_columns(buffer,cols,ncols,maxcols,lerr)
      implicit none
c
      character*(*) buffer
      integer ncols, maxcols, cols(maxcols)
      logical lerr
c
      character*50 item
      integer ipos
c
      lerr  = .FALSE.
      ncols = 0
      ipos  = 1
100      call get_keyword(buffer,item,ipos)
         if( ipos .eq. 0 ) goto 200
*        Latent support for 'ALL' or 'REMAINING' columns
         if( item(1:1) .eq. 'A' .or. item(1:1) .eq. 'R' ) then
            ncols = 1
            cols(1) = -1
            goto 200
         else
            if( ncols+1 .gt. maxcols ) goto 1000
            ncols = ncols + 1
            read(item,*,end=1000,err=1000)
     1            cols(ncols)
         endif
         goto 100
200   continue
      if( ncols .eq. 0 ) lerr = .TRUE.
c
c     Branch over error handler
      goto 1001
c
c     Error handler
1000  continue
      lerr = .TRUE.
c
1001  continue
c
      return
      end
c
      subroutine pm_find_all_y_columns(filename, lu, nskip,
     1            dataset_number, buffer, col_x, n_ycols,
     1            col_y, lineno, maxcols, ierr)
      implicit none
c
      character*(*) filename, buffer
      integer lu, nskip, dataset_number, maxcols,
     1        col_x, col_y(maxcols), lineno, n_ycols, ierr
c
c     Locals
      character*30 item, x_name, y_name, x_units, y_units
      integer i, j, ipos, ncols, nrecl, ioerr, linect
      integer istat, pm_put_error_msg
      integer string_len
      logical input_active, lerr, file_open, is_hspice_output
      double precision value
c
      is_hspice_output = .FALSE.
      input_active = .FALSE.
c
      nrecl=0
      file_open = .FALSE.
      call pm_open_file(lu, filename,
     1                  'OLD','FORMATTED','READ','LIST','SHARED',
     2                  nrecl, ioerr, ierr)
      if( ierr .ne. 0 ) goto 20000
      file_open = .TRUE.
c
      lineno = 0
      linect = 0
c
      do 1000 i=1,nskip
         read(lu,2000,err=20000,iostat=ioerr,end=20000) buffer
         lineno = lineno + 1
1000  continue
c
      input_active = .TRUE.
c
2010  continue
c
      read(lu,2000,err=20000,iostat=ioerr,end=20000) buffer
2000  format(a)
      lineno = lineno + 1
      linect = linect + 1
c
c     Skip comments lines beginning with: ! c C ; # *
c     Note: files created by FORTRAN may have a blank in column 1
      if( buffer(1:1) .eq. ' ' ) then
         i = 2
      else
         i = 1
      endif
      if( buffer(i:i) .eq. '!' .or. buffer(i:i) .eq. 'c' .or.
     1    buffer(i:i) .eq. 'C' .or. buffer(i:i) .eq. ';' .or.
     2    buffer(i:i) .eq. '#' .or. buffer(i:1) .eq. '*' ) goto 2010
c
c     SPICE output file support for W. Anderson
      if( linect .eq. 1 .and. INDEX(buffer,'SPICE') .ne. 0 ) then
         if( INDEX(buffer,'HSPICE') .ne. 0 ) then
            is_hspice_output = .TRUE.
         endif
c        We provide dummy columns, since we're just interested in skipping
c        to the right place in the file...
         call pm_skip_to_spice_xydata(
     1      lu,buffer,lineno,linect,dataset_number,
     2      1, 2, is_hspice_output,
     3      x_name, x_units,
     4      y_name, y_units, lerr)
         if( is_hspice_output ) then
            read(lu,2000,err=20000,iostat=ioerr,end=20000) buffer
         endif
      endif
      if( lerr ) goto 20000
c
c     Skip blank lines
      if( string_len(buffer) .eq. 0 ) goto 2010
c
      if( buffer(1:4) .eq. '.OFF' .or. buffer(1:5) .eq. ' .OFF' .or.
     1    buffer(1:4) .eq. '.off' .or. buffer(1:5) .eq. ' .off') then
         input_active = .FALSE.
      else if( buffer(1:3) .eq. '.ON' .or. buffer(1:4) .eq. ' .ON' .or.
     1         buffer(1:3) .eq. '.on' .or. buffer(1:4) .eq. ' .on')then
         input_active = .TRUE.
      else if( input_active ) then
         call detab(buffer)
         call pm_mangle_asciifile_line(buffer)
c        Exit read loop
         goto 2011
      endif
c
      goto 2010
2011  continue
      close(unit=lu)
      file_open = .FALSE.
c
      ipos = 1
      ncols = 0
c     Loop until we read all the values on the line
1010  call get_keyword(buffer,item,ipos)
         if( ipos .eq. 0 ) goto 1020
         if( ncols .ge. maxcols ) goto 20000
            ncols = ncols + 1
            read(item,*,end=20000,err=20000) value
      goto 1010
1020  continue
c
      if( ncols .le. 1 ) goto 20000
c
      n_ycols = ncols - 1
      j = 1
      do i=1,ncols
         if( i .ne. col_x ) then
            col_y(j) = i
            j = j + 1
         endif
      end do
c
      ierr = 0
      return
c
c     All error conditions
c
20000 continue
      if( file_open ) close(unit=lu)
      ierr = 1
      return
c
      end
c
      subroutine pm_mangle_asciifile_line(buffer)
      implicit none
c
c     Some data files that Warren Anderson creates use a "*" character
c     to denote a empty field.
c
      character*(*) buffer
c
      integer i
c
      do i=1,LEN(buffer)
         if( buffer(i:i) .eq. '*' ) buffer(i:i) = '0'
      end do
c
      return
      end
c
      subroutine pm_get_ascii_datafile_type(itype)
      implicit none
c
      integer itype
c
      include 'const.h'
      include 'sysdep.cmn'
      include 'io.cmn'
c
      integer i
      character*1 ch
c
      call clear_screen
      write(luttyo,100)
100   format(' ','ASCII file types are:'//
     1 ' ','1   X and Y columns have numeric data'/
     1 ' ','2   X and Y columns have numeric data, ',
     1     'sort by Z column data'/
     1 ' ','3   X, Y and Z columns have numeric data ',
     1     '(for 2D data sets)'/
     1 ' ','4   X and Y columns have numeric data, '
     1     'error bars from Z column'/
     1 ' ','5   X has non-blank character data, ',
     1     'Y column has numeric data'//)

c
      call get_ints_with_prompt(itype,1,'Enter number of file type')
c
      return
      end

