rstyx 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []