Class: RStyx::Client::File

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/rstyx/client.rb

Overview

A Styx client’s view of a file. This class should probably never be directly instantiated, but only via Connection#open. The buffering algorithm in use here is somewhat based on the Buffering mix-in module in the Ruby OpenSSL module written by Goto Yuuzou, but modified a bit to provide for offset handling. Note that this class isn’t thread-safe.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(conn, path) ⇒ File

Returns a new instance of File.



487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/rstyx/client.rb', line 487

def initialize(conn, path)
  @conn = conn            # the connection on which the file sits
  @path = path            # pathname of the file
  @fid = -1               # the client's file identifier
  @qid = nil              # the server's unique identifier for this file
  # maximum number of bytes that can be read or written to the file at a time
  @iounit = 0
  @mode = -1              # mode under which the file is opened, -1 == not open
  @offset = 0             # File offset.  This is the same as @boffset only after a seek
  @rboffset = 0           # Read buffer offset
  @eof = false
  @rbuffer = ""
  @sync = false           # whether or not to buffer writes
end

Instance Attribute Details

#connObject (readonly)

Returns the value of attribute conn.



484
485
486
# File 'lib/rstyx/client.rb', line 484

def conn
  @conn
end

#fidObject

Returns the value of attribute fid.



485
486
487
# File 'lib/rstyx/client.rb', line 485

def fid
  @fid
end

#iounitObject

Returns the value of attribute iounit.



485
486
487
# File 'lib/rstyx/client.rb', line 485

def iounit
  @iounit
end

#modeObject

Returns the value of attribute mode.



485
486
487
# File 'lib/rstyx/client.rb', line 485

def mode
  @mode
end

#pathObject (readonly)

Returns the value of attribute path.



484
485
486
# File 'lib/rstyx/client.rb', line 484

def path
  @path
end

#qidObject

Returns the value of attribute qid.



485
486
487
# File 'lib/rstyx/client.rb', line 485

def qid
  @qid
end

#syncObject

Returns the value of attribute sync.



485
486
487
# File 'lib/rstyx/client.rb', line 485

def sync
  @sync
end

Instance Method Details

#<<(s) ⇒ Object



863
864
865
866
# File 'lib/rstyx/client.rb', line 863

def <<(s)
  do_write(s)
  return(self)
end

#closeObject

Close the file. This will flush all unwritten buffered data if any.



601
602
603
604
605
606
607
608
609
610
# File 'lib/rstyx/client.rb', line 601

def close
  flush rescue nil
  if @fid >= 0
    # Clunk the fid
    @conn.tclunk(@fid)
  end
  @fid = -1
  @iounit = 0
  @mode = -1
end

#closed?Boolean

Returns:

  • (Boolean)


612
613
614
# File 'lib/rstyx/client.rb', line 612

def closed?
  return(@mode < 0)
end

#each(eol = $/) ⇒ Object Also known as: each_line

Executes the block for evely line in the Styx file, where lines are separated by eol.



765
766
767
768
769
# File 'lib/rstyx/client.rb', line 765

def each(eol=$/)
  while line = self.gets(eol)
    yield line
  end
end

#eof?Boolean Also known as: eof

Returns:

  • (Boolean)


807
808
809
810
811
812
# File 'lib/rstyx/client.rb', line 807

def eof?
  if !@eof && @rbuffer.empty?
    fill_rbuff
  end
  return(@eof && @rbuffer.empty?)
end

#flushObject



895
896
897
898
899
900
# File 'lib/rstyx/client.rb', line 895

def flush
  osync = @sync
  @sync = true
  do_write ""
  @sync = osync
end

#getcObject



792
793
794
795
# File 'lib/rstyx/client.rb', line 792

def getc
  c = read(1)
  return(c ? c[0] : nil)
end

#gets(eol = $/) ⇒ Object

Reads the next “line” from the Styx file; lines are separated by eol. An eol of nil reads the entire contents. Returns nil if called at end of file.



743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
# File 'lib/rstyx/client.rb', line 743

def gets(eol=$/)
  idx = @rbuffer.index(eol)
  until @eof
    if idx
      break
    end
    fill_rbuff
    idx = @rbuffer.index(eol)
  end
  if eol.is_a?(Regexp)
    size = idx ? idx+$&.size : nil
  else
    size = idx ? idx+eol.size : nil
  end
  @offset += size
  return(consume_rbuff(size))
end

#open(mode, perm, create, &block) ⇒ Object

Open the file on the server. If a block is passed to this method it will pass the file to the block and close the file automatically when the block terminates.

This follows more or less the same semantics as sys-open(2) on Inferno, performing an open with truncate, when a file is opened that doesn’t exist.

XXX - twalk should handle the case of MAXWELEM, as well as for

when the twalk message is too large to fit in the server's
designated msize.
mode

Integer representing the mode - see the constants in common.rb

perm

Permissions of the file (only used on open/create)

create

should we create the file if it doesn’t exist?



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
# File 'lib/rstyx/client.rb', line 519

def open(mode, perm, create, &block)
  dfid = @conn.get_free_fid
  basename = ::File.basename(@path)
  dirname = ::File.dirname(@path)
  # Walk to the dirname first
  twalk = Message::Twalk.new(:fid => @conn.rootfid, :newfid => dfid)
  twalk.path = dirname
  rwalk = @conn.send_message(twalk)
  # if the rwalk has some other length than the number of path
  # elements in the original twalk, we have failed.
  if rwalk.qids.length != twalk.wnames.length
    raise StyxException.new(("#{path} no such file or directory"))
  end
  ropen = fid = nil
  begin
    # Next, walk to the basename from there, using a new fid
    fid = @conn.get_free_fid
    twalk = Message::Twalk.new(:fid => dfid, :newfid => fid)
    twalk.path = basename
    rwalk = @conn.send_message(twalk)
    # Do a Topen if this succeeds
    open_msg = Message::Topen.new(:fid => fid, :mode => mode)
    ropen = @conn.send_message(open_msg)
    @conn.tclunk(dfid)
  rescue Exception => e
    # If we are being directed to create the file if it doesn't
    # already exist, send a Tcreate message, and use the response
    # for it as the ropen (the two classes respond to exactly the
    # same messages--ah the wonders of duck typing!).  If not,
    # or if that fails, this should propagate an exception upwards.
    if create
      # Alter the submitted permissions mask according to
      # the connection's umask
      perm &= ~(@conn.umask)
      create_msg = Message::Tcreate.new(:fid => dfid, :name => basename,
                                        :perm => perm, :mode => mode)
      ropen = @conn.send_message(create_msg)
      fid = dfid
    else
      raise e
    end
  end

  # If we get here, we were able to successfully open or create
  # the file.
  @mode = mode
  @fid = fid
  @qid = ropen.qid
  @iounit = ropen.iounit
  # Determine if the file is actually a directory.  Such a file
  # would have its Qid.qtype high bit set to 1.  In this case, instead
  # of returning self, we return self wrapped in a Directory object.
  retval = self
  if twalk.wnames.length == 0 || (rwalk.qids[-1].qtype & 0x80 != 0)
    retval = Directory.new(self)
  end
  if block_given?
    begin
      yield retval
    ensure
      retval.close
    end
  else
    return(retval)
  end
end


883
884
885
886
887
888
# File 'lib/rstyx/client.rb', line 883

def print(*args)
  s = ""
  args.each{ |arg| s << arg.to_s }
  do_write(s)
  return(nil)
end

#printf(s, *args) ⇒ Object



890
891
892
893
# File 'lib/rstyx/client.rb', line 890

def printf(s, *args)
  do_write(s % args)
  return(nil)
end

#puts(*args) ⇒ Object



868
869
870
871
872
873
874
875
876
877
878
879
880
881
# File 'lib/rstyx/client.rb', line 868

def puts(*args)
  s = ""
  if args.empty?
    s << "\n"
  end
  args.each do |arg|
    s << arg.to_s
    if $/ && /\n\z/ !~ s
      s << "\n"
    end
  end
  do_write(s)
  return(nil)
end

#read(size = -1)) ⇒ Object

Read at most size bytes from the Styx file or to the end of file if omitted. Returns nil if called at end of file.



718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
# File 'lib/rstyx/client.rb', line 718

def read(size=-1)
  until @eof
    # Fill up the buffer until we have at least the requested
    # size, or until end of file if size is negative.
    if size > 0 && size <= @rbuffer.size
      break
    end
    fill_rbuff
  end

  # We managed to slurp the entire file!
  if size < 0
    size = @rbuffer.size
  end

  @offset += size
  retval = consume_rbuff(size) || ""
  (size && retval.empty?) ? nil : retval
end

#readcharObject

Raises:

  • (EOFError)


797
798
799
800
# File 'lib/rstyx/client.rb', line 797

def readchar
  raise EOFError if eof?
  getc
end

#readline(eol = $/) ⇒ Object

Reads a line as with gets, but

Raises:

  • (EOFError)


787
788
789
790
# File 'lib/rstyx/client.rb', line 787

def readline(eol=$/)
  raise EOFError if eof?
  return(gets(eol))
end

#readlines(eol = $/) ⇒ Object

Reads all of the lines in the Styx file, and returns them in an array. Lines are separated by an optional separator eol.



777
778
779
780
781
782
783
# File 'lib/rstyx/client.rb', line 777

def readlines(eol=$/)
  ary = []
  while line = self.gets(eol)
    ary << line
  end
  ary
end

#rewindObject



939
940
941
# File 'lib/rstyx/client.rb', line 939

def rewind
  seek(0, 0)
end

#seek(offset, whence) ⇒ Object

Seek in the file. The whence values may be one of SEEK_SET SEEK_CUR, or SEEK_END, as defined in rstyx/common, and they result in the offset being taken from the beginning of the file, relative to the current offset, or from the end of the file respectively.

XXX: A seek, no matter where it goes, will always invalidate

any buffering.  This is fine for write buffers, but is
somewhat wasteful for the read buffers.


913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
# File 'lib/rstyx/client.rb', line 913

def seek(offset, whence)
  # Before seeking, flush the write buffers
  flush
  s = self.stat
  case whence
  when 0
    @offset = offset
  when 1
    @offset += offset
  when 2
    # We have to obtain the stat of the file to do this kind of seek.
    @offset = s.length + offset
  else
    raise StyxException.new("Invalid seek")
  end
  # After seeking, discard the read buffers
  @rbuffer = ""
  @rboffset = @offset
  @eof = (@offset >= s.length)
  return(@offset)
end

#statObject

Read the Stat information for the file.

returns: the RStyx::Message::Stat instance corresponding to

the file


592
593
594
595
# File 'lib/rstyx/client.rb', line 592

def stat
  rstat = @conn.send_message(Message::Tstat.new(:fid => @fid))
  return(rstat.stat)
end

#sysread(size = -1)) ⇒ Object

Reads size bytes from the Styx file and returns them as a string. Do not mix with other methods that read from the Styx file or you may get unpredictable results.



948
949
950
951
952
953
954
# File 'lib/rstyx/client.rb', line 948

def sysread(size=-1)
  data, @offset = _sysread(size, @offset)
  if data.length == 0
    return(nil)
  end
  return(data)
end

#syswrite(data) ⇒ Object

Writes data to the Styx file. Returns the number of bytes written. do not mix with other methods that write to the Styx file or you may get unpredictable results.



960
961
962
963
# File 'lib/rstyx/client.rb', line 960

def syswrite(data)
  @offset, count = _syswrite(data, @offset)
  return(count)
end

#tellObject



935
936
937
# File 'lib/rstyx/client.rb', line 935

def tell
  return(@offset)
end

#ungetc(c) ⇒ Object



802
803
804
805
# File 'lib/rstyx/client.rb', line 802

def ungetc(c)
  @rbuffer[0,0] = c.chr
  @offset -= 1
end

#write(s) ⇒ Object



858
859
860
861
# File 'lib/rstyx/client.rb', line 858

def write(s)
  do_write(s)
  return(s.length)
end