rstyx 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rstyx/client.rb CHANGED
@@ -24,9 +24,10 @@
24
24
  # Copyright:: Copyright (c) 2005 Rafael R. Sevilla
25
25
  # License:: GNU Lesser General Public License
26
26
  #
27
- # $Id: client.rb,v 1.5 2005/09/30 08:07:46 dido Exp $
27
+ # $Id: client.rb,v 1.10 2005/11/01 13:02:18 dido Exp $
28
28
  #
29
29
 
30
+ require 'English'
30
31
  require 'socket'
31
32
  require 'thread'
32
33
  require 'timeout'
@@ -67,17 +68,23 @@ module RStyx
67
68
  end
68
69
  return(val)
69
70
  end
71
+
70
72
  end
71
73
 
72
74
  ##
73
75
  # An abstract class for Styx connections. DO NOT INSTANTIATE THIS
74
76
  # CLASS DIRECTLY!
75
77
  #
78
+ # A +Connection+ subclass instance acts in the same way as the
79
+ # +File+ class would under Ruby, and many of the class methods for
80
+ # File are implemented as instance methods for Connection, and are
81
+ # relevant to the Styx filesystem the Connection is connected to.
82
+ # There are a few differences: for instance, new is not equivalent
83
+ # to open, for obvious reasons.
84
+ #
76
85
  class Connection
77
86
  attr_reader :msize, :version, :rootfid, :user
78
87
 
79
- TIMEOUT = 60
80
-
81
88
  ##
82
89
  # Create a new Styx connection. If a block is passed, yield the
83
90
  # new connection to the block and do a disconnect when the block
@@ -288,35 +295,154 @@ module RStyx
288
295
  #
289
296
  # +path+:: the path to the Styx file to be opened
290
297
  # +mode+:: the mode to open the file (which can be r, w, r+, or e)
298
+ # +perm+:: the (optional) permissions bit mask. This is only
299
+ # relevant if the open creates a file.
300
+ # +rfid+:: the root fid. If this is nil, use the root fid we got
301
+ # when we connected.
302
+ #
303
+ # All parameters are optional. If no path is specified, it defaults
304
+ # to reading the root directory on the Styx server.
291
305
  #
292
- # FIXME: This doesn't deal with the case where MAXWELEM is reached yet.
306
+ # FIXME: This doesn't deal with the case where MAXWELEM is reached
307
+ # yet.
308
+ # TODO: Allow the caller to make use of the Styx file access mode
309
+ # values (OREAD, OWRITE, ORDWR, OEXEC, OTRUNC, ORCLOSE).
293
310
  #
294
- def open(path, mode='r')
311
+ def open(path="", mode='r', perm=0644, rfid=nil)
295
312
  # get a new fid for the file
296
313
  fid = get_free_fid
297
- # Walk the fid to the given file
314
+
315
+ if rfid.nil?
316
+ rfid = @rootfid
317
+ end
318
+
319
+ #
320
+ # The mode string is the standard Ruby mode string, viz.
321
+ # "r" - read-only, start at beginning of file
322
+ # "r+" - read/write, start at beginning of file
323
+ # "w" - write only, truncate existing file or create it if it
324
+ # doesn't exist.
325
+ # "w+" - read/write, truncate existing file or create it if it
326
+ # doesn't exist
327
+ # "a" - write only, starts at end of file if it exists otherwise
328
+ # creates a new file for writing.
329
+ # "a+" - read/write, starts at end of file if it exists, otherwise
330
+ # creates a new file for writing.
331
+ #
332
+ # There is also a mode specific to RStyx, not a standard mode
333
+ # for Ruby:
334
+ #
335
+ # "e" - execute the file
336
+ #
337
+ # The "b" suffix on DOS/Windows is not recognized as valid.
338
+ #
339
+ read = true
340
+ write = false
341
+ create = false
342
+ truncate = false
343
+ append = false
344
+ mval = Message::Topen::OREAD
345
+ case mode
346
+ when "r"
347
+ # all default above
348
+ when "r+"
349
+ write = true
350
+ mval = Message::Topen::ORDWR
351
+ when "w"
352
+ read = false
353
+ write = true
354
+ create = true
355
+ truncate = true
356
+ mval = Message::Topen::OWRITE
357
+ when "w+"
358
+ read = true
359
+ write = true
360
+ create = true
361
+ truncate = true
362
+ mval = Message::Topen::ORDWR
363
+ when "a"
364
+ read = false
365
+ write = true
366
+ create = true
367
+ append = true
368
+ mval = Message::Topen::OWRITE
369
+ when "a+"
370
+ read = true
371
+ write = true
372
+ create = true
373
+ append = true
374
+ mval = Message::Topen::ORDWR
375
+ when "e"
376
+ read = true
377
+ write = true
378
+ mval = Message::Topen::OEXEC
379
+ else
380
+ raise StyxException.new("Invalid mode string '#{mode}'")
381
+ end
382
+
383
+ if truncate
384
+ mval |= Message::Topen::OTRUNC
385
+ end
386
+
387
+ iclass = StyxIO
298
388
  begin
299
- twalk = Message::Twalk.new(@rootfid, fid, path)
389
+ twalk = Message::Twalk.new(rfid, fid, path)
300
390
  rwalk = send_message(twalk)
301
391
  # if the rwalk has some other length than the number of path
302
392
  # elements in the original twalk, we have failed.
303
393
  if rwalk.qids.length != twalk.path_elements.length
304
394
  raise StyxException.new(sprintf("%s: no such file or directory", twalk.path_elements[rwalk.qids.length]))
305
395
  end
306
- # TODO: if the file is a directory?
307
- # Convert the mode string into a mode number
308
- mval = case mode
309
- when 'r': Message::Topen::OREAD
310
- when 'w': Message::Topen::OWRITE
311
- when 'r+': Message::Topen::ORDWR
312
- when 'e': Message::Topen::OEXEC
313
- end
396
+ open_msg = Message::Topen.new(fid, mval)
397
+ # Determine if the file is actually a directory. Such a file
398
+ # would have its Qid.qtype high bit set to 1. In this case,
399
+ # mode can only be 'r', and instead of a StyxIO, a StyxDir
400
+ # instance is created instead.
401
+ #
402
+ # If the number of path elements involved is zero, assume a
403
+ # directory, as the root fid is always one.
404
+ #
405
+ if twalk.path_elements.length == 0 ||
406
+ rwalk.qids[twalk.path_elements.length-1].qtype & 0x80 != 0
407
+ iclass = StyxDir
408
+ end
409
+
410
+ rescue StyxException => se
411
+ # If the walk generated an error, see if the create flag
412
+ # was true. If so, the error may be ignored, and the
413
+ # open_msgclass set to Message::Tcreate. Otherwise, return
414
+ # the fid and reraise the exception.
415
+ if create
416
+ # Walk to the directory of the file to be made, if possible
417
+ begin
418
+ rwalk = send_message(Message::Twalk.new(rfid, fid,
419
+ File.dirname(path)))
420
+ rescue StyxException => se
421
+ return_fid(fid)
422
+ raise se # reraise the exception
423
+ end
424
+ open_msg = Message::Tcreate.new(fid, File.basename(path),
425
+ mval, perm)
426
+ else
427
+ return_fid(fid)
428
+ raise se # reraise the exception
429
+ end
430
+ end
431
+
432
+ begin
314
433
  # Create, open, and return a StyxIO object
315
- ropen = send_message(Message::Topen.new(fid, mval))
316
- fp = StyxIO.new(self, path, fid, ropen.iounit, mode)
434
+ ropen = send_message(open_msg)
435
+ fp = iclass.new(self, path, fid, ropen.iounit, mode)
436
+
437
+ # if we're supposed to append, seek to the end of the file
438
+ if append
439
+ rstat = send_message(Message::Tstat.new(fid))
440
+ fp.seek(rstat.stat.length, :seek_set)
441
+ end
442
+
317
443
  if block_given?
318
444
  begin
319
- yield fp
445
+ yield fp
320
446
  ensure
321
447
  fp.close
322
448
  end
@@ -325,7 +451,68 @@ module RStyx
325
451
  end
326
452
  rescue StyxException => se
327
453
  return_fid(fid)
328
- raise se # reraise the exception
454
+ raise se # reraise the exception
455
+ end
456
+ end
457
+
458
+ ##
459
+ # Delete the files specified. Unlike File.delete, this method
460
+ # should also work properly on directories
461
+ #
462
+ def delete(*files)
463
+ files.each do |file|
464
+ fid = get_free_fid
465
+ # walk to the file to be deleted
466
+ begin
467
+ rwalk = send_message(Message::Twalk.new(@rootfid, fid, file))
468
+ rescue StyxException => se
469
+ return_fid(fid)
470
+ raise se
471
+ end
472
+
473
+ begin
474
+ rremove = send_message(Message::Tremove.new(fid))
475
+ return_fid(fid)
476
+ rescue StyxException => se
477
+ # because the Twalk succeeded, we need to clunk the fid
478
+ begin
479
+ rclunk = send_message(Message::Tclunk.new(fid))
480
+ rescue StyxException => se
481
+ return_fid(fid)
482
+ end
483
+ raise se
484
+ end
485
+ end
486
+ end
487
+
488
+ alias rmdir delete
489
+ alias unlink delete
490
+
491
+ ##
492
+ # Create a new directory named by +dirname+, with permissions
493
+ # specified by an optional parameter +perm+.
494
+ #
495
+ def mkdir(dirname, perm=0755)
496
+ begin
497
+ fid = get_free_fid
498
+ perm |= Message::Tcreate::DMDIR
499
+ rwalk = send_message(Message::Twalk.new(@rootfid, fid,
500
+ File.dirname(dirname)))
501
+ rescue StyxException => se
502
+ return_fid(fid)
503
+ raise se
504
+ end
505
+
506
+ begin
507
+ rcreate = send_message(Message::Tcreate.new(fid,
508
+ File.basename(dirname),
509
+ Message::Topen::OREAD,
510
+ perm))
511
+ rescue StyxException => se
512
+ return_fid(fid)
513
+ raise se
514
+ ensure
515
+ rclunk = send_message(Message::Tclunk.new(fid))
329
516
  end
330
517
  end
331
518
 
@@ -361,13 +548,85 @@ module RStyx
361
548
  end
362
549
 
363
550
  ##
364
- # A file or directory on a Styx server. This should probably not
365
- # be instantiated directly, but only by the use of Connection#open.
551
+ # Internally used class for buffers
552
+ class Buffer
553
+ attr_reader :bufsize
554
+
555
+ def initialize(bufsize)
556
+ @bufsize = bufsize # maximum size of the buffer
557
+ @data = "" # data in the buffer
558
+ end
559
+
560
+ ##
561
+ # Returns true if the buffer is empty
562
+ #
563
+ def empty?
564
+ return(@data.length == 0)
565
+ end
566
+
567
+ ##
568
+ # Returns true if the buffer is full
569
+ def full?
570
+ return(@data.length == @bufsize)
571
+ end
572
+
573
+ ##
574
+ # How much data is needed to fill the buffer?
575
+ #
576
+ def slack
577
+ return(@bufsize - @data.length)
578
+ end
579
+
580
+ ##
581
+ # Empty the buffer. Return all the data in the buffer
582
+ #
583
+ def empty
584
+ retval = @data
585
+ @data = ""
586
+ return(retval)
587
+ end
588
+
589
+ ##
590
+ # Consume data from the buffer. Returns a string of at most +size+
591
+ # length from the buffer.
592
+ #
593
+ def consume(size)
594
+ retval = @data[0..(size-1)]
595
+ @data = @data[size..@data.length]
596
+ if @data == nil # if the buffer goes empty
597
+ @data = ""
598
+ end
599
+ return(retval)
600
+ end
601
+
602
+ ##
603
+ # Write data to the buffer. Returns the data that was not actually
604
+ # written to the buffer (the excess) if the buffer is full.
605
+ def fill(data)
606
+ if data.length > slack
607
+ excess = data[slack..(data.length)]
608
+ data = data[0..(slack-1)]
609
+ end
610
+ @data << data
611
+ return(excess)
612
+ end
613
+
614
+ end
615
+
616
+ ##
617
+ # A file on a Styx server. This should probably not be instantiated
618
+ # directly, but only by the use of Connection#open.
366
619
  #
367
620
  # TODO: This should later become a true subclass of IO.
368
621
  #
369
622
  class StyxIO
370
- attr_reader :name, :mode, :closed, :offset
623
+ attr_reader :name, :mode, :closed, :offset, :iounit, :fid
624
+ attr_accessor :sync
625
+
626
+ # Whether or not to buffer writes. Unlike regular IO objects,
627
+ # StyxIO has this true by default.
628
+ attr_accessor :sync
629
+ include Enumerable
371
630
 
372
631
  def initialize(conn, name, fid, iounit, mode="r")
373
632
  @conn = conn
@@ -377,25 +636,25 @@ module RStyx
377
636
  @fid = fid
378
637
  @iounit = iounit # maximum payload of a Twrite or Rread message
379
638
  @offset = 0 # file offset
380
- end
639
+ @sync = true
381
640
 
382
- ##
383
- # closes the file. Once this has been done, no more read or write
384
- # operations should be possible.
385
- #
386
- def close
387
- flush # flush any bytes buffered (later)
388
- rclunk = @conn.send_message(Message::Tclunk.new(@fid))
389
- # FIXME: abort all outstanding messages with flushes
641
+ @buffer = Buffer.new(iounit) # the read buffer
642
+ @boffset = 0 # the offset of the last buffered read or write
643
+ @lastop = :none # the last operation performed
644
+ @sync = false # Buffer or not buffer writes
390
645
  end
391
646
 
392
647
  ##
393
- # Read at most +size+ bytes from the current file position.
648
+ # Read at most +size+ bytes from +offset+
394
649
  # If the size argument is negative or omitted, read until EOF.
650
+ # This is an unbuffered read! This function should probably
651
+ # not be used directly.
395
652
  #
396
653
  # +size+:: number of bytes to read from the file
654
+ # +offset+:: the offset to read from.
655
+ # return:: the data followed by the new offset
397
656
  #
398
- def read(size=-1)
657
+ def sysread_int(size=-1, offset=0)
399
658
  contents = ""
400
659
  bytes_to_read = size
401
660
  loop do
@@ -406,27 +665,31 @@ module RStyx
406
665
  else
407
666
  n = bytes_to_read
408
667
  end
409
- rread = @conn.send_message(Message::Tread.new(@fid, @offset, n))
668
+ rread = @conn.send_message(Message::Tread.new(@fid, offset, n))
410
669
  if rread.data.length == 0
411
670
  break # EOF
412
671
  end
413
- @offset += rread.data.length
672
+ offset += rread.data.length
414
673
  contents << rread.data
415
674
  if size >= 0
416
675
  bytes_to_read -= rread.data.length
417
676
  end
418
677
  end
419
- return(contents)
678
+ return([contents, offset])
420
679
  end
421
680
 
422
681
  ##
423
682
  #
424
- # Write data to the file at the current offset. No buffering is
425
- # performed (yet).
683
+ # Write data to the file at +offset+. No buffering is
684
+ # performed. This function should probably not be used
685
+ # directly.
426
686
  #
427
687
  # +str+:: data to be written
688
+ # +offset+:: the offset to write at
428
689
  #
429
- def write(str)
690
+ # returns the new offset
691
+ #
692
+ def syswrite_int(str, offset)
430
693
  pos = 0
431
694
  loop do
432
695
  bytes_left = str.length - pos
@@ -437,28 +700,197 @@ module RStyx
437
700
  else
438
701
  n = bytes_left
439
702
  end
440
- rwrite = @conn.send_message(Message::Twrite.new(@fid, @offset,
703
+ rwrite = @conn.send_message(Message::Twrite.new(@fid, offset,
441
704
  str[pos..(pos+n)]))
442
705
  if rwrite.count != n
443
706
  raise StyxException.new("error writing data")
444
707
  end
445
708
  pos += n
446
- @offset += n
709
+ offset += n
447
710
  end
711
+ return(offset)
712
+ end
448
713
 
714
+ public
715
+
716
+ ##
717
+ # closes the file. Once this has been done, no more read or write
718
+ # operations should be possible.
719
+ #
720
+ def close
721
+ flush # flush any bytes buffered
722
+ rclunk = @conn.send_message(Message::Tclunk.new(@fid))
723
+ # FIXME: abort all outstanding messages with flushes
724
+ end
725
+
726
+ ##
727
+ # Read at most +size+ bytes from the current file position.
728
+ # If the size argument is negative or omitted, read until EOF.
729
+ # Returns nil if we read from the end of stream.
730
+ #
731
+ # This is a buffered read.
732
+ #
733
+ # +size+:: number of bytes to read from the file
734
+ #
735
+ def read(size=-1)
736
+ # flush any buffered data first, so the buffer becomes empty
737
+ if @lastop == :write
738
+ flush
739
+ end
740
+ @lastop = :read
741
+
742
+ bytes_to_read = size
743
+ contents = ""
744
+ loop do
745
+ if @buffer.empty?
746
+ # fill the buffer if the buffer goes empty
747
+ rdata, @boffset = sysread_int(@buffer.slack, @boffset)
748
+ if rdata.length == 0
749
+ break
750
+ end
751
+ @buffer.fill(rdata)
752
+ end
753
+
754
+ d = ""
755
+
756
+ if size < 0 || bytes_to_read > @buffer.bufsize
757
+ d = @buffer.empty
758
+ else
759
+ d = @buffer.consume(bytes_to_read)
760
+ end
761
+
762
+ @offset += d.length
763
+ contents << d
764
+ if size >= 0
765
+ bytes_to_read -= d.length
766
+ end
767
+
768
+ if bytes_to_read == 0
769
+ break
770
+ end
771
+ end
772
+ if (contents.length == 0)
773
+ return(nil)
774
+ end
775
+ return(contents)
776
+ end
777
+
778
+ ##
779
+ # Do an unbuffered read.
780
+ #
781
+ def sysread(size=-1)
782
+ unless @buffer.empty?
783
+ raise IOError.new("sysread for buffered IO")
784
+ end
785
+ data, @offset = sysread_int(size, @offset)
786
+ if (data.length == 0)
787
+ return(nil)
788
+ end
789
+ return(data)
790
+ end
791
+
792
+ ##
793
+ # Calls the given block once for each byte (0..255) in the StyxIO,
794
+ # passing the byte as an argument. The Styx file must be opened
795
+ # for reading.
796
+ #
797
+ def each_byte
798
+ if !block_given?
799
+ raise LocalJumpError("no block given")
800
+ end
801
+
802
+ while (!(c=read(1)).nil?)
803
+ yield c[0]
804
+ end
805
+ end
806
+
807
+ ##
808
+ # Reads the next "line" from the Styx file; lines are separated by
809
+ # sep_str. A separator of nil reads the entire contents, [and a
810
+ # zero length separator reads the input a paragraph at a time (two
811
+ # successive newlines in the input separate paragraphs) TODO: this
812
+ # needs to be implemented]. The Styxfile
813
+ # must be opened for reading. The line read in will be returned and
814
+ # also assigned to $LAST_READ_LINE. Returns nil if called at end
815
+ # of file.
816
+ #
817
+ def gets(sep_str=$RS)
818
+ line = ""
819
+ while (!(c=read(1)).nil?)
820
+ line << c
821
+ if c == $RS
822
+ break
823
+ end
824
+ end
825
+ if line.length == 0
826
+ return(nil)
827
+ end
828
+ return(line)
829
+ end
830
+
831
+ ##
832
+ # Executes the passed block for every line in styxio, where lines
833
+ # are separated by sep_str. The Styx file must be opened for
834
+ # reading.
835
+ #
836
+ def each
837
+ if !block_given?
838
+ raise LocalJumpError("no block given")
839
+ end
840
+
841
+ while (!(line = gets).nil?)
842
+ yield line
843
+ end
844
+ end
845
+
846
+ ##
847
+ # A buffered write of data to the file.
848
+ #
849
+ def write(data)
850
+ if @lastop == :read
851
+ @buffer.empty # empty the buffer
852
+ end
853
+ @lastop = :write
854
+
855
+ # If the sync flag is set, simply write the data straight
856
+ # to the file with no buffering at all.
857
+ if @sync
858
+ @offset = syswrite_int(data, @offset)
859
+ return(data.length)
860
+ end
861
+
862
+ # If the sync flag is not set, try to write the data to the buffer
863
+ # until the buffer becomes full.
864
+ bytes_written = data.length
865
+ while !data.nil?
866
+ if @buffer.full?
867
+ # flush the buffer if the buffer fills up
868
+ flush
869
+ end
870
+ data = @buffer.fill(data)
871
+ end
872
+ @offset += bytes_written
873
+ return(bytes_written)
449
874
  end
450
875
 
451
876
  ##
452
877
  # Flushes any buffered data
453
878
  #
454
879
  def flush
455
- # TODO
880
+ if @lastop == :write
881
+ @boffset = syswrite_int(@buffer.empty, @boffset)
882
+ return(self)
883
+ end
456
884
  end
457
885
 
458
886
  ##
459
887
  # Move to a new seek position
460
888
  #
889
+ # TODO: implement :seek_end
890
+ #
461
891
  def seek(offset, whence=:seek_set)
892
+ # flush the buffers before doing the seek
893
+ flush
462
894
  case whence
463
895
  when :seek_set: @offset = offset
464
896
  when :seek_cur: @offset += offset
@@ -466,8 +898,17 @@ module RStyx
466
898
  when :seek_end: raise StyxException.new("Can't seek from end of file")
467
899
  else raise StyxException.new("whence must be :seek_set, :seek_cur, or :seek_end")
468
900
  end
901
+ @boffset = @offset
902
+ @buffer.empty
469
903
  end
470
-
904
+
905
+ ##
906
+ # Seek to the start of the file
907
+ #
908
+ def rewind
909
+ seek(0, :seek_set)
910
+ end
911
+
471
912
  ##
472
913
  # Return the current file position
473
914
  #
@@ -485,15 +926,93 @@ module RStyx
485
926
 
486
927
  end
487
928
 
488
- end # module Client
929
+ ##
930
+ # A directory on a Styx server. This obtains the entries inside
931
+ # a directory. This actually delegates to StyxIO.
932
+ #
933
+ class StyxDir
934
+ include Enumerable
489
935
 
490
- end # module RStyx
936
+ def initialize(conn, name, fid, iounit, mode="r")
937
+ @io = StyxIO.new(conn, name, fid, iounit, mode)
938
+ # directory entry buffer
939
+ @read_direntries = []
940
+ end
941
+
942
+ def close
943
+ @io.close
944
+ end
945
+
946
+ ##
947
+ # get the fid corresponding to this directory
948
+ #
949
+ def fid
950
+ @io.fid
951
+ end
952
+
953
+ ##
954
+ # Read the next directory entry from the dir and return the file
955
+ # name as a string. Returns nil at the end of stream.
956
+ #
957
+ def read
958
+ # if there are directory entries left over from the previous
959
+ # read that have not yet been returned, return them.
960
+ if @read_direntries.length != 0
961
+ return(@read_direntries.shift.name)
962
+ end
963
+ # read iounit bytes from the directory--this must be unbuffered
964
+ data = @io.read(@io.iounit)
965
+
966
+ if (data.nil?)
967
+ return(nil)
968
+ end
969
+
970
+ # decode the directory entries in the iounit
971
+ while data.length != 0
972
+ delen = data[0..1].unpack("v")[0]
973
+ edirent = data[0..(delen + 1)]
974
+ data = data[(delen + 2)..(data.length)]
975
+ @read_direntries << Message::DirEntry.decode(edirent)
976
+ end
977
+ de = @read_direntries.shift
978
+ if (de.nil?)
979
+ return(nil)
980
+ end
981
+ return(de.name)
982
+ end
983
+
984
+ ##
985
+ # Seek to the beginning of the directory. This is the only type of
986
+ # seek allowed.
987
+ #
988
+ def rewind
989
+ @io.rewind
990
+ end
991
+
992
+ ##
993
+ # same as StyxIO#stat
994
+ #
995
+ def stat
996
+ return(@io.stat)
997
+ end
998
+
999
+ ##
1000
+ # Call the block once for each entry in the directory, passing the
1001
+ # filename of each entry as a parameter to the block.
1002
+ #
1003
+ def each
1004
+ if !block_given?
1005
+ raise LocalJumpError("no block given")
1006
+ end
1007
+
1008
+ self.rewind
1009
+ while (!(de = self.read).nil?)
1010
+ yield de
1011
+ end
1012
+ end
491
1013
 
492
- if $0 == __FILE__
493
- RStyx::Client::TCPConnection.new("127.0.0.1", 1234) do |c|
494
- c.open("hello.txt", "r+") do |fp|
495
- puts fp.stat.to_s
496
1014
  end
497
- end
498
- end
499
1015
 
1016
+ end # module Client
1017
+
1018
+ end # module RStyx
@@ -24,7 +24,7 @@
24
24
  # Copyright:: Copyright (c) 2005 Rafael R. Sevilla
25
25
  # License:: GNU Lesser General Public License
26
26
  #
27
- # $Id: messages.rb,v 1.24 2005/09/30 05:35:42 dido Exp $
27
+ # $Id: messages.rb,v 1.26 2005/11/01 11:40:26 dido Exp $
28
28
  #
29
29
 
30
30
  require 'rstyx/errors'
@@ -163,7 +163,7 @@ module RStyx
163
163
  str << Util.strpack(@gid)
164
164
  str << Util.strpack(@muid)
165
165
  @size = str.length
166
- return([size].pack("v") + str)
166
+ return([@size].pack("v") + str)
167
167
  end
168
168
 
169
169
  ##
@@ -1340,6 +1340,7 @@ module RStyx
1340
1340
  @stat = stat
1341
1341
  @mtype = RSTAT
1342
1342
  @messagebody = @stat.to_bytes
1343
+ @messagebody = [@stat.size + 2].pack("v") + @messagebody
1343
1344
  @length = 7 + @messagebody.length
1344
1345
  @body = sprintf("%s", @stat)
1345
1346
  end
@@ -1353,7 +1354,7 @@ module RStyx
1353
1354
  #
1354
1355
  def Rstat.decode(message)
1355
1356
  len = message[0..3].unpack("V")[0]
1356
- stat = DirEntry.decode(message[7..len])
1357
+ stat = DirEntry.decode(message[9..len])
1357
1358
  return(Rstat.new(stat))
1358
1359
  end
1359
1360
 
@@ -1375,7 +1376,9 @@ module RStyx
1375
1376
  @fid = fid
1376
1377
  @stat = stat
1377
1378
  @mtype = TWSTAT
1378
- @messagebody = [@fid].pack("V") + @stat.to_bytes
1379
+ @messagebody = @stat.to_bytes
1380
+ @messagebody = [@fid].pack("V") +
1381
+ [@stat.size + 2].pack("v") + @messagebody
1379
1382
  @length = 7 + @messagebody.length
1380
1383
  @body = sprintf("%s, %s", @fid, @stat)
1381
1384
  end
@@ -1390,7 +1393,7 @@ module RStyx
1390
1393
  def Twstat.decode(message)
1391
1394
  len = message[0..3].unpack("V")[0]
1392
1395
  fid = message[7..10].unpack("V")[0]
1393
- stat = DirEntry.decode(message[11..len])
1396
+ stat = DirEntry.decode(message[13..len])
1394
1397
  return(Twstat.new(fid, stat))
1395
1398
  end
1396
1399
 
data/lib/rstyx/version.rb CHANGED
@@ -20,14 +20,14 @@
20
20
  #
21
21
  # RStyx version code
22
22
  #
23
- # $Id: version.rb,v 1.1 2005/09/30 08:13:09 dido Exp $
23
+ # $Id: version.rb,v 1.2 2005/11/01 13:17:27 dido Exp $
24
24
  #
25
25
 
26
26
  module RStyx
27
27
  module Version
28
28
 
29
29
  MAJOR = 0
30
- MINOR = 1
30
+ MINOR = 2
31
31
  TINY = 0
32
32
 
33
33
  # The version of RStyx in use.
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Copyright (C) 2005 Rafael Sevilla
4
+ # This file is part of RStyx
5
+ #
6
+ # RStyx is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU Lesser General Public License as
8
+ # published by the Free Software Foundation; either version 2.1
9
+ # of the License, or (at your option) any later version.
10
+ #
11
+ # RStyx is distributed in the hope that it will be useful, but
12
+ # WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Lesser General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Lesser General Public
17
+ # License along with RStyx; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19
+ # 02111-1307 USA.
20
+ #
21
+ # Unit tests for Styx client.
22
+ #
23
+ # To use these tests, redefine CONNECT_HOST and CONNECT_PORT to point to
24
+ # a Styx server that will accept connections with no authentication and
25
+ # will allow write access. I'm currently using 4th Edition Inferno hosted
26
+ # on GNU/Linux to do this
27
+ #
28
+ # $Id: tc_client.rb,v 1.2 2005/11/01 13:17:04 dido Exp $
29
+ #
30
+ require 'test/unit'
31
+ require 'rstyx'
32
+
33
+ class ClientTest < Test::Unit::TestCase
34
+ CONNECT_HOST = "127.0.0.1"
35
+ CONNECT_PORT = "1234"
36
+ TEST_DIRECTORY = "/rstyx_test_dir"
37
+ TEST_FILE = TEST_DIRECTORY + "/test.file"
38
+
39
+ def test_client
40
+ RStyx::Client::TCPConnection.new(CONNECT_HOST, CONNECT_PORT) do |c|
41
+ begin
42
+ # Create the directory
43
+ assert_nothing_raised { c.mkdir(TEST_DIRECTORY) }
44
+ # Try to create the directory again (this should result in an error)
45
+ assert_raise(RStyx::StyxException) { c.mkdir(TEST_DIRECTORY) }
46
+
47
+ # open a file in the directory for writing, and fill it with data
48
+ assert_nothing_raised do
49
+ c.open(TEST_FILE, "w") do |fp|
50
+ 0.upto(1024) do |i|
51
+ fp.write(sprintf("%04x\n", i))
52
+ end
53
+ end
54
+ end
55
+
56
+ # Open the file for reading, and check that the data is correct
57
+ c.open(TEST_FILE, "r") do |fp|
58
+ i = 0
59
+ fp.each do |line|
60
+ assert(line == sprintf("%04x\n", i))
61
+ i += 1
62
+ end
63
+ end
64
+ ensure
65
+ assert_nothing_raised { c.delete(TEST_FILE) }
66
+ assert_raise(RStyx::StyxException) { c.delete(TEST_FILE) }
67
+ # Make sure the directories are deleted.
68
+ assert_nothing_raised { c.rmdir("/rstyx_test_dir") }
69
+ # Deleting the directory twice should generate an error
70
+ assert_raise(RStyx::StyxException) { c.rmdir(TEST_DIRECTORY) }
71
+ end
72
+ end
73
+ end
74
+ end
data/tests/tc_message.rb CHANGED
@@ -20,7 +20,7 @@
20
20
  #
21
21
  # Unit tests for Styx messages
22
22
  #
23
- # $Id: tc_message.rb,v 1.23 2005/09/28 17:56:58 dido Exp $
23
+ # $Id: tc_message.rb,v 1.25 2005/11/01 11:44:06 dido Exp $
24
24
  #
25
25
  require 'test/unit'
26
26
  require 'rstyx/messages'
@@ -1138,10 +1138,11 @@ class MessageTest < Test::Unit::TestCase
1138
1138
  de.muid = "quux"
1139
1139
  rs = RStyx::Message::Rstat.new(de)
1140
1140
  bytes = rs.to_bytes(0x1234)
1141
- expect = "\x7d\x34\x12\x3c\x00\x34\x12\xab\x90\x78\x56\x01\xef\xbe\xad\xde\xee\xee\xff\xc0\xce\xfa\xed\xfe\xf0\xde\xbc\x9a\xef\xbe\xad\xde\xbe\xba\xfe\xca\x10\x32\x54\x76\x98\xba\xdc\xfe\x03\x00foo\x03\x00bar\x03\x00baz\x04\x00quux"
1141
+ expect = "\x7d\x34\x12\x3e\x00\x3c\x00\x34\x12\xab\x90\x78\x56\x01\xef\xbe\xad\xde\xee\xee\xff\xc0\xce\xfa\xed\xfe\xf0\xde\xbc\x9a\xef\xbe\xad\xde\xbe\xba\xfe\xca\x10\x32\x54\x76\x98\xba\xdc\xfe\x03\x00foo\x03\x00bar\x03\x00baz\x04\x00quux"
1142
1142
  len = expect.length + 4
1143
1143
  packlen = [len].pack("V")
1144
1144
  expect = packlen + expect
1145
+ puts ""
1145
1146
  assert(expect == bytes)
1146
1147
 
1147
1148
  # decode expect
@@ -1207,7 +1208,7 @@ class MessageTest < Test::Unit::TestCase
1207
1208
  de.muid = "quux"
1208
1209
  tw = RStyx::Message::Twstat.new(0x12345678, de)
1209
1210
  bytes = tw.to_bytes(0x1234)
1210
- expect = "\x7e\x34\x12\x78\x56\x34\x12\x3c\x00\x34\x12\xab\x90\x78\x56\x01\xef\xbe\xad\xde\xee\xee\xff\xc0\xce\xfa\xed\xfe\xf0\xde\xbc\x9a\xef\xbe\xad\xde\xbe\xba\xfe\xca\x10\x32\x54\x76\x98\xba\xdc\xfe\x03\x00foo\x03\x00bar\x03\x00baz\x04\x00quux"
1211
+ expect = "\x7e\x34\x12\x78\x56\x34\x12\x3e\x00\x3c\x00\x34\x12\xab\x90\x78\x56\x01\xef\xbe\xad\xde\xee\xee\xff\xc0\xce\xfa\xed\xfe\xf0\xde\xbc\x9a\xef\xbe\xad\xde\xbe\xba\xfe\xca\x10\x32\x54\x76\x98\xba\xdc\xfe\x03\x00foo\x03\x00bar\x03\x00baz\x04\x00quux"
1211
1212
  len = expect.length + 4
1212
1213
  packlen = [len].pack("V")
1213
1214
  expect = packlen + expect
metadata CHANGED
@@ -1,13 +1,13 @@
1
- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: rstyx
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2005-10-01 00:00:00 +08:00
6
+ version: 0.2.0
7
+ date: 2005-11-01 00:00:00 +08:00
8
8
  summary: RStyx is an implementation of the Styx/9P2000 distributed filesystem protocol for Ruby.
9
9
  require_paths:
10
- - lib
10
+ - lib
11
11
  email: dido@imperium.ph
12
12
  homepage: http://rubyforge.org/projects/rstyx
13
13
  rubyforge_project:
@@ -18,35 +18,31 @@ bindir: bin
18
18
  has_rdoc: true
19
19
  required_ruby_version: !ruby/object:Gem::Version::Requirement
20
20
  requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
21
+ -
22
+ - ">"
23
+ - !ruby/object:Gem::Version
24
+ version: 0.0.0
24
25
  version:
25
26
  platform: ruby
26
27
  signing_key:
27
28
  cert_chain:
28
29
  authors:
29
- - Rafael Sevilla
30
+ - Rafael Sevilla
30
31
  files:
31
- - lib/rstyx
32
- - lib/rstyx.rb
33
- - lib/rstyx/messages.rb
34
- - lib/rstyx/client.rb
35
- - lib/rstyx/version.rb
36
- - lib/rstyx/errors.rb
37
- - tests/tc_message.rb
32
+ - lib/rstyx
33
+ - lib/rstyx.rb
34
+ - lib/rstyx/messages.rb
35
+ - lib/rstyx/client.rb
36
+ - lib/rstyx/version.rb
37
+ - lib/rstyx/errors.rb
38
+ - tests/tc_client.rb
39
+ - tests/tc_message.rb
38
40
  test_files: []
39
-
40
41
  rdoc_options:
41
- - --title
42
- - RStyx -- Styx/9P2000 for Ruby
42
+ - "--title"
43
+ - "RStyx -- Styx/9P2000 for Ruby"
43
44
  extra_rdoc_files: []
44
-
45
45
  executables: []
46
-
47
46
  extensions: []
48
-
49
47
  requirements: []
50
-
51
- dependencies: []
52
-
48
+ dependencies: []