From 0a8b6ea5dc6a9d56638b985dd67b0c45f167e381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Zurcher?= Date: Sat, 28 Feb 2009 21:19:37 +0100 Subject: mmap goes rakeified, build and test OK --- Changes | 4 + Rakefile | 20 + doc/mmap.html | 272 ++++++ doc/mmap.rd | 253 ++++++ ext/mmap/extconf.rb | 33 + ext/mmap/mmap.c | 2303 +++++++++++++++++++++++++++++++++++++++++++++++++++ extconf.rb | 83 -- gem_spec.rb | 28 + mmap.c | 2303 --------------------------------------------------- mmap.html | 272 ------ mmap.rd | 253 ------ prj_cfg.rb | 35 + setup.rb | 1596 +++++++++++++++++++++++++++++++++++ tasks/csts.rake | 72 ++ tasks/gem.rake | 22 + tasks/rcov.rake | 9 + tasks/rdoc.rake | 17 + tasks/show.rake | 21 + tasks/stats.rake | 25 + tasks/test.rake | 12 + tasks/todo.rake | 6 + test/mmapt.rb | 380 --------- test/runit_.rb | 45 - test/test_mmap.rb | 364 ++++++++ todo.rb | 51 ++ 25 files changed, 5143 insertions(+), 3336 deletions(-) create mode 100644 Rakefile create mode 100644 doc/mmap.html create mode 100644 doc/mmap.rd create mode 100644 ext/mmap/extconf.rb create mode 100644 ext/mmap/mmap.c delete mode 100644 extconf.rb create mode 100644 gem_spec.rb delete mode 100644 mmap.c delete mode 100644 mmap.html delete mode 100644 mmap.rd create mode 100644 prj_cfg.rb create mode 100644 setup.rb create mode 100644 tasks/csts.rake create mode 100644 tasks/gem.rake create mode 100644 tasks/rcov.rake create mode 100644 tasks/rdoc.rake create mode 100644 tasks/show.rake create mode 100644 tasks/stats.rake create mode 100644 tasks/test.rake create mode 100644 tasks/todo.rake delete mode 100644 test/mmapt.rb delete mode 100644 test/runit_.rb create mode 100644 test/test_mmap.rb create mode 100644 todo.rb diff --git a/Changes b/Changes index e1325d2..0f97df6 100644 --- a/Changes +++ b/Changes @@ -53,3 +53,7 @@ --- 0.2.0 * adapted for 1.8.0 + +--- 0.2.7 + +* ts is gone we'll all miss his skill and humour diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..dd8ed8c --- /dev/null +++ b/Rakefile @@ -0,0 +1,20 @@ + +require 'rake/clean' +require 'rake/gempackagetask' + +require 'prj_cfg' +load 'tasks/csts.rake' +require 'gem_spec' + + +Dir.glob( 'tasks/*.rake').each do |fn| + next if fn =~ /csts.rake/ + begin + load fn + rescue LoadError + puts "task #{fn.split('/')[1] } unavailable" + end +end + +task :default => [:test] + diff --git a/doc/mmap.html b/doc/mmap.html new file mode 100644 index 0000000..2d78ddc --- /dev/null +++ b/doc/mmap.html @@ -0,0 +1,272 @@ + + + + +Untitled + + +

Mmap

+

Download

+

The Mmap class implement memory-mapped file objects

+

WARNING

+

The variables $' and $` are not available with gsub! and sub!

+

SuperClass

+

Object

+

Included Modules

+ +

Class Methods

+
+
lockall(flag)
+
+disable paging of all pages mapped. flag can be +Mmap::MCL_CURRENT or Mmap::MCL_FUTURE
+
new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {})
+
new(nil, length, protection = Mmap::MAP_SHARED, options = {})
+
+create a new Mmap object +
+
file
+
+Pathname of the file, if nil is given an anonymous map +is created Mmanp::MAP_ANON +
+
mode
+
+Mode to open the file, it can be "r", "w", "rw", "a" +
+
protection
+
+specify the nature of the mapping +
+
Mmap::MAP_SHARED
+
+Creates a mapping that's shared with all other processes +mapping the same areas of the file. +The default value is Mmap::MAP_SHARED +
+
Mmap::MAP_PRIVATE
+
+Creates a private copy-on-write mapping, so changes to the +contents of the mmap object will be private to this process +
+
+
+
options
+
+Hash. If one of the options length or offset +is specified it will not possible to modify the size of +the mapped file. +
+
length
+
+Maps length bytes from the file +
+
offset
+
+The mapping begin at offset +
+
advice
+
+The type of the access (see #madvise) +
+
+
+
+
unlockall
+
+reenable paging
+
+

Methods

+
+
extend(count)
+
+add count bytes to the file (i.e. pre-extend the file)
+
madvise(advice)
+
+advice can have the value Mmap::MADV_NORMAL, +Mmap::MADV_RANDOM, Mmap::MADV_SEQUENTIAL, +Mmap::MADV_WILLNEED, Mmap::MADV_DONTNEED
+
mprotect(mode)
+
+change the mode, value must be "r", "w" or "rw"
+
mlock
+
+disable paging
+
msync
+
flush
+
+flush the file
+
munlock
+
+reenable paging
+
munmap
+
+terminate the association
+
+

Other methods with the same syntax than for the class String

+
+
self == other
+
+comparison
+
self > other
+
+comparison
+
self >= other
+
+comparison
+
self < other
+
+comparison
+
self <= other
+
+comparison
+
self === other
+
+used for case comparison
+
self << other
+
+append other to self
+
self =~ other
+
+return an index of the match
+
self[nth]
+
+retrieve the nth character
+
self[start..last]
+
+return a substring from start to last
+
self[start, length]
+
+return a substring of lenght characters from start
+
self[nth] = val
+
+change the nth character with val
+
self[start..last] = val
+
+change substring from start to last with val
+
self[start, len] = val
+
+replace length characters from start with val.
+
self <=> other
+
+comparison : return -1, 0, 1
+
casecmp(other) >= 1.7.1
+
concat(other)
+
+append the contents of other
+
capitalize!
+
+change the first character to uppercase letter
+
chop!
+
+chop off the last character
+
chomp!([rs])
+
+chop off the line ending character, specified by rs
+
count(o1 [, o2, ...])
+
+each parameter defines a set of character to count
+
crypt(salt)
+
+crypt with salt
+
delete!(str)
+
+delete every characters included in str
+
downcase!
+
+change all uppercase character to lowercase character
+
each_byte {|char|...}
+
+iterate on each byte
+
each([rs]) {|line|...}
+
each_line([rs]) {|line|...}
+
+iterate on each line
+
empty?
+
+return true if the file is empty
+
freeze
+
+freeze the current file
+
frozen
+
+return true if the file is frozen
+
gsub!(pattern, replace)
+
+global substitution
+
gsub!(pattern) {|str|...}
+
+global substitution
+
include?(other)
+
+return true if other is found
+
index(substr[, pos])
+
+return the index of substr
+
insert(index, str) >= 1.7.1
+
+insert str at index
+
length
+
+return the size of the file
+
reverse!
+
+reverse the content of the file
+
rindex(substr[, pos])
+
+return the index of the last occurrence of substr
+
scan(pattern)
+
+return an array of all occurence matched by pattern
+
scan(pattern) {|str| ...}
+
+iterate through the file, matching the pattern
+
size
+
+return the size of the file
+
slice
+
+same than []
+
slice!
+
+delete the specified portion of the file
+
split([sep[, limit]])
+
+splits into a list of strings and return this array
+
squeeze!([str])
+
+squeezes sequences of the same characters which is included in str
+
strip!
+
+removes leading and trailing whitespace
+
sub!(pattern, replace)
+
+substitution
+
sub!(pattern) {|str| ...}
+
+substitution
+
sum([bits])
+
+return a checksum
+
swapcase!
+
+replaces all lowercase characters to uppercase characters, and vice-versa
+
tr!(search, replace)
+
+translate the character from search to replace
+
tr_s!(search, replace)
+
+translate the character from search to replace, then +squeeze sequence of the same characters
+
upcase!
+
+replaces all lowercase characters to downcase characters
+
+ + + diff --git a/doc/mmap.rd b/doc/mmap.rd new file mode 100644 index 0000000..622ad89 --- /dev/null +++ b/doc/mmap.rd @@ -0,0 +1,253 @@ +=begin += Mmap + +(()) + +The Mmap class implement memory-mapped file objects + +=== WARNING +=== The variables $' and $` are not available with gsub! and sub! + +== SuperClass + +Object + +== Included Modules + +* Comparable +* Enumerable + +== Class Methods + +--- lockall(flag) + disable paging of all pages mapped. ((|flag|)) can be + ((|Mmap::MCL_CURRENT|)) or ((|Mmap::MCL_FUTURE|)) + +--- new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {}) +--- new(nil, length, protection = Mmap::MAP_SHARED, options = {}) + create a new Mmap object + + : ((|file|)) + Pathname of the file, if ((|nil|)) is given an anonymous map + is created ((|Mmanp::MAP_ANON|)) + + : ((|mode|)) + Mode to open the file, it can be "r", "w", "rw", "a" + + : ((|protection|)) + specify the nature of the mapping + + : ((|Mmap::MAP_SHARED|)) + Creates a mapping that's shared with all other processes + mapping the same areas of the file. + The default value is ((|Mmap::MAP_SHARED|)) + + : ((|Mmap::MAP_PRIVATE|)) + Creates a private copy-on-write mapping, so changes to the + contents of the mmap object will be private to this process + + : ((|options|)) + Hash. If one of the options ((|length|)) or ((|offset|)) + is specified it will not possible to modify the size of + the mapped file. + + : ((|length|)) + Maps ((|length|)) bytes from the file + + : ((|offset|)) + The mapping begin at ((|offset|)) + + : ((|advice|)) + The type of the access (see #madvise) + + +--- unlockall + reenable paging + +== Methods + +--- extend(count) + add ((|count|)) bytes to the file (i.e. pre-extend the file) + +--- madvise(advice) + ((|advice|)) can have the value ((|Mmap::MADV_NORMAL|)), + ((|Mmap::MADV_RANDOM|)), ((|Mmap::MADV_SEQUENTIAL|)), + ((|Mmap::MADV_WILLNEED|)), ((|Mmap::MADV_DONTNEED|)) + +--- mprotect(mode) + change the mode, value must be "r", "w" or "rw" + +--- mlock + disable paging + +--- msync +--- flush + flush the file + +--- munlock + reenable paging + +--- munmap + terminate the association + +=== Other methods with the same syntax than for the class String + + +--- self == other + comparison + +--- self > other + comparison + +--- self >= other + comparison + +--- self < other + comparison + +--- self <= other + comparison + +--- self === other + used for ((|case|)) comparison + +--- self << other + append ((|other|)) to ((|self|)) + +--- self =~ other + return an index of the match + +--- self[nth] + retrieve the ((|nth|)) character + +--- self[start..last] + return a substring from ((|start|)) to ((|last|)) + +--- self[start, length] + return a substring of ((|lenght|)) characters from ((|start|)) + +--- self[nth] = val + change the ((|nth|)) character with ((|val|)) + +--- self[start..last] = val + change substring from ((|start|)) to ((|last|)) with ((|val|)) + +--- self[start, len] = val + replace ((|length|)) characters from ((|start|)) with ((|val|)). + +--- self <=> other + comparison : return -1, 0, 1 + +--- casecmp(other) >= 1.7.1 + +--- concat(other) + append the contents of ((|other|)) + +--- capitalize! + change the first character to uppercase letter + +--- chop! + chop off the last character + +--- chomp!([rs]) + chop off the line ending character, specified by ((|rs|)) + +--- count(o1 [, o2, ...]) + each parameter defines a set of character to count + +--- crypt(salt) + crypt with ((|salt|)) + +--- delete!(str) + delete every characters included in ((|str|)) + +--- downcase! + change all uppercase character to lowercase character + +--- each_byte {|char|...} + iterate on each byte + +--- each([rs]) {|line|...} +--- each_line([rs]) {|line|...} + iterate on each line + +--- empty? + return ((|true|)) if the file is empty + +--- freeze + freeze the current file + +--- frozen + return ((|true|)) if the file is frozen + +--- gsub!(pattern, replace) + global substitution + +--- gsub!(pattern) {|str|...} + global substitution + +--- include?(other) + return ((|true|)) if ((|other|)) is found + +--- index(substr[, pos]) + return the index of ((|substr|)) + +--- insert(index, str) >= 1.7.1 + insert ((|str|)) at ((|index|)) + +--- length + return the size of the file + +--- reverse! + reverse the content of the file + +--- rindex(substr[, pos]) + return the index of the last occurrence of ((|substr|)) + +--- scan(pattern) + return an array of all occurence matched by ((|pattern|)) + +--- scan(pattern) {|str| ...} + iterate through the file, matching the ((|pattern|)) + +--- size + return the size of the file + +--- slice + same than ((|[]|)) + +--- slice! + delete the specified portion of the file + +--- split([sep[, limit]]) + splits into a list of strings and return this array + +--- squeeze!([str]) + squeezes sequences of the same characters which is included in ((|str|)) + +--- strip! + removes leading and trailing whitespace + +--- sub!(pattern, replace) + substitution + +--- sub!(pattern) {|str| ...} + substitution + +--- sum([bits]) + return a checksum + +--- swapcase! + replaces all lowercase characters to uppercase characters, and vice-versa + +--- tr!(search, replace) + translate the character from ((|search|)) to ((|replace|)) + +--- tr_s!(search, replace) + translate the character from ((|search|)) to ((|replace|)), then + squeeze sequence of the same characters + +--- upcase! + replaces all lowercase characters to downcase characters + +=end diff --git a/ext/mmap/extconf.rb b/ext/mmap/extconf.rb new file mode 100644 index 0000000..87e8088 --- /dev/null +++ b/ext/mmap/extconf.rb @@ -0,0 +1,33 @@ +#!/usr/bin/ruby +ARGV.collect! {|x| x.sub(/^--with-mmap-prefix=/, "--with-mmap-dir=") } + +require 'mkmf' + +if unknown = enable_config("unknown") + libs = if CONFIG.key?("LIBRUBYARG_STATIC") + Config::expand(CONFIG["LIBRUBYARG_STATIC"].dup).sub(/^-l/, '') + else + Config::expand(CONFIG["LIBRUBYARG"].dup).sub(/^lib([^.]*).*/, '\\1') + end + unknown = find_library(libs, "ruby_init", + Config::expand(CONFIG["archdir"].dup)) +end + +dir_config("mmap") + +["lstrip", "match", "insert", "casecmp"].each do |func| + if "aa".respond_to?(func) + $CFLAGS += " -DHAVE_RB_STR_#{func.upcase}" + end +end + +if enable_config("ipc") + unless have_func("semctl") && have_func("shmctl") + $stderr.puts "\tIPC will not be available" + end +end + +$CFLAGS += " -DRUBYLIBDIR='\"#{CONFIG['rubylibdir']}\"'" + +create_makefile "mmap" + diff --git a/ext/mmap/mmap.c b/ext/mmap/mmap.c new file mode 100644 index 0000000..d77603f --- /dev/null +++ b/ext/mmap/mmap.c @@ -0,0 +1,2303 @@ +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_SEMCTL && HAVE_SHMCTL +#include +#include +#include +#endif + +#include +#include +#include + +#ifndef StringValue +#define StringValue(x) do { \ + if (TYPE(x) != T_STRING) x = rb_str_to_str(x); \ +} while (0) +#endif + +#ifndef StringValuePtr +#define StringValuePtr(x) STR2CSTR(x) +#endif + +#ifndef SafeStringValue +#define SafeStringValue(x) Check_SafeStr(x) +#endif + +#ifndef MADV_NORMAL +#ifdef POSIX_MADV_NORMAL +#define MADV_NORMAL POSIX_MADV_NORMAL +#define MADV_RANDOM POSIX_MADV_RANDOM +#define MADV_SEQUENTIAL POSIX_MADV_SEQUENTIAL +#define MADV_WILLNEED POSIX_MADV_WILLNEED +#define MADV_DONTNEED POSIX_MADV_DONTNEED +#define madvise posix_madvise +#endif +#endif + +#define BEG(no) regs->beg[no] +#define END(no) regs->end[no] + +#ifndef MMAP_RETTYPE +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309 +#endif /* !_POSIX_C_SOURCE */ +#ifdef _POSIX_VERSION +#if _POSIX_VERSION >= 199309 +#define MMAP_RETTYPE void * +#endif /* _POSIX_VERSION >= 199309 */ +#endif /* _POSIX_VERSION */ +#endif /* !MMAP_RETTYPE */ + +#ifndef MMAP_RETTYPE +#define MMAP_RETTYPE caddr_t +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((caddr_t)-1) +#endif /* !MAP_FAILED */ + +#ifndef MAP_ANON +#ifdef MAP_ANONYMOUS +#define MAP_ANON MAP_ANONYMOUS +#endif +#endif + +static VALUE mm_cMap; + +#define EXP_INCR_SIZE 4096 + +typedef struct { + MMAP_RETTYPE addr; + int smode, pmode, vscope; + int advice, flag; + VALUE key; + int semid, shmid; + size_t len, real, incr; + off_t offset; + char *path, *template; +} mm_mmap; + +typedef struct { + int count; + mm_mmap *t; +} mm_ipc; + +typedef struct { + VALUE obj, *argv; + int flag, id, argc; +} mm_bang; + +#define MM_MODIFY 1 +#define MM_ORIGIN 2 +#define MM_CHANGE (MM_MODIFY | 4) +#define MM_PROTECT 8 + +#define MM_FROZEN (1<<0) +#define MM_FIXED (1<<1) +#define MM_ANON (1<<2) +#define MM_LOCK (1<<3) +#define MM_IPC (1<<4) +#define MM_TMP (1<<5) + +#if HAVE_SEMCTL && HAVE_SHMCTL +static char template[1024]; + +union semun +{ + int val; + struct semid_ds *buf; + unsigned short int *array; + struct seminfo *__buf; +}; +#endif + +static void +mm_free(i_mm) + mm_ipc *i_mm; +{ +#if HAVE_SEMCTL && HAVE_SHMCTL + if (i_mm->t->flag & MM_IPC) { + struct shmid_ds buf; + + if (shmctl(i_mm->t->shmid, IPC_STAT, &buf) != -1) { + if (buf.shm_nattch == 1 && (i_mm->t->flag & MM_TMP)) { + semctl(i_mm->t->semid, 0, IPC_RMID); + if (i_mm->t->template) { + unlink(i_mm->t->template); + free(i_mm->t->template); + } + } + } + shmdt(i_mm->t); + } + else { + free(i_mm->t); + } +#endif + if (i_mm->t->path) { + munmap(i_mm->t->addr, i_mm->t->len); + if (i_mm->t->path != (char *)-1) { + if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE && + truncate(i_mm->t->path, i_mm->t->real) == -1) { + free(i_mm->t->path); + free(i_mm); + rb_raise(rb_eTypeError, "truncate"); + } + free(i_mm->t->path); + } + } + free(i_mm); +} + +static void +mm_lock(i_mm, wait_lock) + mm_ipc *i_mm; + int wait_lock; +{ +#if HAVE_SEMCTL && HAVE_SHMCTL + struct sembuf sem_op; + + if (i_mm->t->flag & MM_IPC) { + i_mm->count++; + if (i_mm->count == 1) { + retry: + sem_op.sem_num = 0; + sem_op.sem_op = -1; + sem_op.sem_flg = IPC_NOWAIT; + if (semop(i_mm->t->semid, &sem_op, 1) == -1) { + if (errno == EAGAIN) { + if (!wait_lock) { + rb_raise(rb_const_get(rb_mErrno, rb_intern("EAGAIN")), "EAGAIN"); + } + rb_thread_sleep(1); + goto retry; + } + rb_sys_fail("semop()"); + } + } + } +#endif +} + +static void +mm_unlock(i_mm) + mm_ipc *i_mm; +{ +#if HAVE_SEMCTL && HAVE_SHMCTL + struct sembuf sem_op; + + if (i_mm->t->flag & MM_IPC) { + i_mm->count--; + if (!i_mm->count) { + retry: + sem_op.sem_num = 0; + sem_op.sem_op = 1; + sem_op.sem_flg = IPC_NOWAIT; + if (semop(i_mm->t->semid, &sem_op, 1) == -1) { + if (errno == EAGAIN) { + rb_thread_sleep(1); + goto retry; + } + rb_sys_fail("semop()"); + } + } + } +#endif +} + +#define GetMmap(obj, i_mm, t_modify) \ + Data_Get_Struct(obj, mm_ipc, i_mm); \ + if (!i_mm->t->path) { \ + rb_raise(rb_eIOError, "unmapped file"); \ + } \ + if ((t_modify & MM_MODIFY) && (i_mm->t->flag & MM_FROZEN)) { \ + rb_error_frozen("mmap"); \ + } + +static VALUE +mm_vunlock(obj) + VALUE obj; +{ + mm_ipc *i_mm; + + GetMmap(obj, i_mm, 0); + mm_unlock(i_mm); + return Qnil; +} + +static VALUE +mm_semlock(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + mm_ipc *i_mm; + + GetMmap(obj, i_mm, 0); + if (!(i_mm->t->flag & MM_IPC)) { + rb_warning("useless use of #semlock"); + rb_yield(obj); + } + else { +#if HAVE_SEMCTL && HAVE_SHMCTL + VALUE a; + int wait_lock = Qtrue; + + if (rb_scan_args(argc, argv, "01", &a)) { + wait_lock = RTEST(a); + } + mm_lock(i_mm, wait_lock); + rb_ensure(rb_yield, obj, mm_vunlock, obj); +#endif + } + return Qnil; +} + +static VALUE +mm_ipc_key(obj) + VALUE obj; +{ + mm_ipc *i_mm; + + GetMmap(obj, i_mm, 0); + if (i_mm->t->flag & MM_IPC) { + return INT2NUM(i_mm->t->key); + } + return INT2NUM(-1); +} + +static VALUE +mm_unmap(obj) + VALUE obj; +{ + mm_ipc *i_mm; + + GetMmap(obj, i_mm, 0); + if (i_mm->t->path) { + mm_lock(i_mm, Qtrue); + munmap(i_mm->t->addr, i_mm->t->len); + if (i_mm->t->path != (char *)-1) { + if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE && + truncate(i_mm->t->path, i_mm->t->real) == -1) { + rb_raise(rb_eTypeError, "truncate"); + } + free(i_mm->t->path); + } + i_mm->t->path = '\0'; + mm_unlock(i_mm); + } + return Qnil; +} + +static VALUE +mm_freeze(obj) + VALUE obj; +{ + mm_ipc *i_mm; + rb_obj_freeze(obj); + GetMmap(obj, i_mm, 0); + i_mm->t->flag |= MM_FROZEN; + return obj; +} + +static VALUE +mm_str(obj, modify) + VALUE obj; + int modify; +{ + mm_ipc *i_mm; + VALUE ret = Qnil; + + GetMmap(obj, i_mm, modify & ~MM_ORIGIN); + if (modify & MM_MODIFY) { + if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap"); + if (!OBJ_TAINTED(ret) && rb_safe_level() >= 4) + rb_raise(rb_eSecurityError, "Insecure: can't modify mmap"); + } +#if HAVE_RB_DEFINE_ALLOC_FUNC + ret = rb_obj_alloc(rb_cString); + if (rb_obj_tainted(obj)) { + OBJ_TAINT(ret); + } +#else + if (rb_obj_tainted(obj)) { + ret = rb_tainted_str_new2(""); + } + else { + ret = rb_str_new2(""); + } + free(RSTRING(ret)->ptr); +#endif + RSTRING(ret)->ptr = i_mm->t->addr; + RSTRING(ret)->len = i_mm->t->real; + if (modify & MM_ORIGIN) { +#if HAVE_RB_DEFINE_ALLOC_FUNC + RSTRING(ret)->aux.shared = ret; + FL_SET(ret, ELTS_SHARED); +#else + RSTRING(ret)->orig = ret; +#endif + } + if (i_mm->t->flag & MM_FROZEN) { + ret = rb_obj_freeze(ret); + } + return ret; +} + +static VALUE +mm_to_str(obj) + VALUE obj; +{ + return mm_str(obj, MM_ORIGIN); +} + +extern char *ruby_strdup(); + +typedef struct { + mm_ipc *i_mm; + size_t len; +} mm_st; + +static VALUE +mm_i_expand(st_mm) + mm_st *st_mm; +{ + int fd; + mm_ipc *i_mm = st_mm->i_mm; + size_t len = st_mm->len; + + if (munmap(i_mm->t->addr, i_mm->t->len)) { + rb_raise(rb_eArgError, "munmap failed"); + } + if ((fd = open(i_mm->t->path, i_mm->t->smode)) == -1) { + rb_raise(rb_eArgError, "Can't open %s", i_mm->t->path); + } + if (len > i_mm->t->len) { + if (lseek(fd, len - i_mm->t->len - 1, SEEK_END) == -1) { + rb_raise(rb_eIOError, "Can't lseek %d", len - i_mm->t->len - 1); + } + if (write(fd, "\000", 1) != 1) { + rb_raise(rb_eIOError, "Can't extend %s", i_mm->t->path); + } + } + else if (len < i_mm->t->len && truncate(i_mm->t->path, len) == -1) { + rb_raise(rb_eIOError, "Can't truncate %s", i_mm->t->path); + } + i_mm->t->addr = mmap(0, len, i_mm->t->pmode, i_mm->t->vscope, fd, i_mm->t->offset); + close(fd); + if (i_mm->t->addr == MAP_FAILED) { + rb_raise(rb_eArgError, "mmap failed"); + } +#ifdef MADV_NORMAL + if (i_mm->t->advice && madvise(i_mm->t->addr, len, i_mm->t->advice) == -1) { + rb_raise(rb_eArgError, "madvise(%d)", errno); + } +#endif + if ((i_mm->t->flag & MM_LOCK) && mlock(i_mm->t->addr, len) == -1) { + rb_raise(rb_eArgError, "mlock(%d)", errno); + } + i_mm->t->len = len; + return Qnil; +} + +static void +mm_expandf(i_mm, len) + mm_ipc *i_mm; + size_t len; +{ + int status; + mm_st st_mm; + + if (i_mm->t->vscope == MAP_PRIVATE) { + rb_raise(rb_eTypeError, "expand for a private map"); + } + if (i_mm->t->flag & MM_FIXED) { + rb_raise(rb_eTypeError, "expand for a fixed map"); + } + if (!i_mm->t->path || i_mm->t->path == (char *)-1) { + rb_raise(rb_eTypeError, "expand for an anonymous map"); + } + st_mm.i_mm = i_mm; + st_mm.len = len; + if (i_mm->t->flag & MM_IPC) { + mm_lock(i_mm, Qtrue); + rb_protect(mm_i_expand, (VALUE)&st_mm, &status); + mm_unlock(i_mm); + if (status) { + rb_jump_tag(status); + } + } + else { + mm_i_expand(&st_mm); + } +} + +static void +mm_realloc(i_mm, len) + mm_ipc *i_mm; + size_t len; +{ + if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap"); + if (len > i_mm->t->len) { + if ((len - i_mm->t->len) < i_mm->t->incr) { + len = i_mm->t->len + i_mm->t->incr; + } + mm_expandf(i_mm, len); + } +} + +static VALUE +mm_extend(obj, a) + VALUE obj, a; +{ + mm_ipc *i_mm; + long len; + + GetMmap(obj, i_mm, MM_MODIFY); + len = NUM2LONG(a); + if (len > 0) { + mm_expandf(i_mm, i_mm->t->len + len); + } + return UINT2NUM(i_mm->t->len); +} + +static VALUE +mm_i_options(arg, obj) + VALUE arg, obj; +{ + mm_ipc *i_mm; + char *options; + VALUE key, value; + + Data_Get_Struct(obj, mm_ipc, i_mm); + key = rb_ary_entry(arg, 0); + value = rb_ary_entry(arg, 1); + key = rb_obj_as_string(key); + options = StringValuePtr(key); + if (strcmp(options, "length") == 0) { + i_mm->t->len = NUM2UINT(value); + if (i_mm->t->len <= 0) { + rb_raise(rb_eArgError, "Invalid value for length %d", i_mm->t->len); + } + i_mm->t->flag |= MM_FIXED; + } + else if (strcmp(options, "offset") == 0) { + i_mm->t->offset = NUM2INT(value); + if (i_mm->t->offset < 0) { + rb_raise(rb_eArgError, "Invalid value for offset %d", i_mm->t->offset); + } + i_mm->t->flag |= MM_FIXED; + } + else if (strcmp(options, "advice") == 0) { + i_mm->t->advice = NUM2INT(value); + } + else if (strcmp(options, "increment") == 0) { + int incr = NUM2INT(value); + if (incr < 0) { + rb_raise(rb_eArgError, "Invalid value for increment %d", incr); + } + i_mm->t->incr = incr; + } + else if (strcmp(options, "initialize") == 0) { + } +#if HAVE_SEMCTL && HAVE_SHMCTL + else if (strcmp(options, "ipc") == 0) { + if (value != Qtrue && TYPE(value) != T_HASH) { + rb_raise(rb_eArgError, "Expected an Hash for :ipc"); + } + i_mm->t->shmid = value; + i_mm->t->flag |= (MM_IPC | MM_TMP); + } +#endif + else { + rb_warning("Unknown option `%s'", options); + } + return Qnil; +} + +#if HAVE_SEMCTL && HAVE_SHMCTL + +static VALUE +mm_i_ipc(arg, obj) + VALUE arg, obj; +{ + mm_ipc *i_mm; + char *options; + VALUE key, value; + + Data_Get_Struct(obj, mm_ipc, i_mm); + key = rb_ary_entry(arg, 0); + value = rb_ary_entry(arg, 1); + key = rb_obj_as_string(key); + options = StringValuePtr(key); + if (strcmp(options, "key") == 0) { + i_mm->t->key = rb_funcall2(value, rb_intern("to_int"), 0, 0); + } + else if (strcmp(options, "permanent") == 0) { + if (RTEST(value)) { + i_mm->t->flag &= ~MM_TMP; + } + } + else if (strcmp(options, "mode") == 0) { + i_mm->t->semid = NUM2INT(value); + } + else { + rb_warning("Unknown option `%s'", options); + } + return Qnil; +} + +#endif + +static VALUE +mm_s_new(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + VALUE res = rb_funcall2(obj, rb_intern("allocate"), 0, 0); + rb_obj_call_init(res, argc, argv); + return res; +} + +static VALUE +mm_s_alloc(obj) + VALUE obj; +{ + VALUE res; + mm_ipc *i_mm; + + res = Data_Make_Struct(obj, mm_ipc, 0, mm_free, i_mm); + i_mm->t = ALLOC_N(mm_mmap, 1); + MEMZERO(i_mm->t, mm_mmap, 1); + i_mm->t->incr = EXP_INCR_SIZE; + return res; +} + +static VALUE +mm_init(argc, argv, obj) + VALUE obj, *argv; + int argc; +{ + struct stat st; + int fd, smode = 0, pmode = 0, vscope, perm, init; + MMAP_RETTYPE addr; + VALUE fname, fdv, vmode, scope, options; + mm_ipc *i_mm; + char *path, *mode; + size_t size = 0; + off_t offset; + int anonymous; + + options = Qnil; + if (argc > 1 && TYPE(argv[argc - 1]) == T_HASH) { + options = argv[argc - 1]; + argc--; + } + rb_scan_args(argc, argv, "12", &fname, &vmode, &scope); + vscope = 0; + path = 0; + fd = -1; + anonymous = 0; + fdv = Qnil; +#ifdef MAP_ANON + if (NIL_P(fname)) { + vscope = MAP_ANON | MAP_SHARED; + anonymous = 1; + } + else +#endif + { + if (rb_safe_level() > 0 && OBJ_TAINTED(fname)){ + rb_raise(rb_eSecurityError, "Insecure operation"); + } + rb_secure(4); + if (rb_respond_to(fname, rb_intern("fileno"))) { + fdv = rb_funcall2(fname, rb_intern("fileno"), 0, 0); + } + if (NIL_P(fdv)) { + fname = rb_str_to_str(fname); + SafeStringValue(fname); + path = StringValuePtr(fname); + } + else { + fd = NUM2INT(fdv); + if (fd < 0) { + rb_raise(rb_eArgError, "invalid file descriptor %d", fd); + } + } + if (!NIL_P(scope)) { + vscope = NUM2INT(scope); +#ifdef MAP_ANON + if (vscope & MAP_ANON) { + rb_raise(rb_eArgError, "filename specified for an anonymous map"); + } +#endif + } + } + vscope |= NIL_P(scope) ? MAP_SHARED : NUM2INT(scope); + size = 0; + perm = 0666; + if (!anonymous) { + if (NIL_P(vmode)) { + mode = "r"; + } + else if (rb_respond_to(vmode, rb_intern("to_ary"))) { + VALUE tmp; + + vmode = rb_convert_type(vmode, T_ARRAY, "Array", "to_ary"); + if (RARRAY(vmode)->len != 2) { + rb_raise(rb_eArgError, "Invalid length %d (expected 2)", + RARRAY(vmode)->len); + } + tmp = RARRAY(vmode)->ptr[0]; + mode = StringValuePtr(tmp); + perm = NUM2INT(RARRAY(vmode)->ptr[1]); + } + else { + mode = StringValuePtr(vmode); + } + if (strcmp(mode, "r") == 0) { + smode = O_RDONLY; + pmode = PROT_READ; + } + else if (strcmp(mode, "w") == 0) { + smode = O_RDWR | O_TRUNC; + pmode = PROT_READ | PROT_WRITE; + } + else if (strcmp(mode, "rw") == 0 || strcmp(mode, "wr") == 0) { + smode = O_RDWR; + pmode = PROT_READ | PROT_WRITE; + } + else if (strcmp(mode, "a") == 0) { + smode = O_RDWR | O_CREAT; + pmode = PROT_READ | PROT_WRITE; + } + else { + rb_raise(rb_eArgError, "Invalid mode %s", mode); + } + if (NIL_P(fdv)) { + if ((fd = open(path, smode, perm)) == -1) { + rb_raise(rb_eArgError, "Can't open %s", path); + } + } + if (fstat(fd, &st) == -1) { + rb_raise(rb_eArgError, "Can't stat %s", path); + } + size = st.st_size; + } + else { + fd = -1; + if (!NIL_P(vmode) && TYPE(vmode) != T_STRING) { + size = NUM2INT(vmode); + } + } + Data_Get_Struct(obj, mm_ipc, i_mm); + if (i_mm->t->flag & MM_FROZEN) { + rb_raise(rb_eArgError, "frozen mmap"); + } + i_mm->t->shmid = 0; + i_mm->t->semid = 0; + offset = 0; + if (options != Qnil) { + rb_iterate(rb_each, options, mm_i_options, obj); + if (path && (i_mm->t->len + i_mm->t->offset) > st.st_size) { + rb_raise(rb_eArgError, "invalid value for length (%d) or offset (%d)", + i_mm->t->len, i_mm->t->offset); + } + if (i_mm->t->len) size = i_mm->t->len; + offset = i_mm->t->offset; +#if HAVE_SEMCTL && HAVE_SHMCTL + if (i_mm->t->flag & MM_IPC) { + key_t key; + int shmid, semid, mode; + union semun sem_val; + struct shmid_ds buf; + mm_mmap *data; + + if (!(vscope & MAP_SHARED)) { + rb_warning("Probably it will not do what you expect ..."); + } + i_mm->t->key = -1; + i_mm->t->semid = 0; + if (TYPE(i_mm->t->shmid) == T_HASH) { + rb_iterate(rb_each, i_mm->t->shmid, mm_i_ipc, obj); + } + i_mm->t->shmid = 0; + if (i_mm->t->semid) { + mode = i_mm->t->semid; + i_mm->t->semid = 0; + } + else { + mode = 0644; + } + if ((int)i_mm->t->key <= 0) { + mode |= IPC_CREAT; + strcpy(template, "/tmp/ruby_mmap.XXXXXX"); + if (mkstemp(template) == -1) { + rb_sys_fail("mkstemp()"); + } + if ((key = ftok(template, 'R')) == -1) { + rb_sys_fail("ftok()"); + } + } + else { + key = (key_t)i_mm->t->key; + } + if ((shmid = shmget(key, sizeof(mm_ipc), mode)) == -1) { + rb_sys_fail("shmget()"); + } + data = shmat(shmid, (void *)0, 0); + if (data == (mm_mmap *)-1) { + rb_sys_fail("shmat()"); + } + if (i_mm->t->flag & MM_TMP) { + if (shmctl(shmid, IPC_RMID, &buf) == -1) { + rb_sys_fail("shmctl()"); + } + } + if ((semid = semget(key, 1, mode)) == -1) { + rb_sys_fail("semget()"); + } + if (mode & IPC_CREAT) { + sem_val.val = 1; + if (semctl(semid, 0, SETVAL, sem_val) == -1) { + rb_sys_fail("semctl()"); + } + } + memcpy(data, i_mm->t, sizeof(mm_mmap)); + free(i_mm->t); + i_mm->t = data; + i_mm->t->key = key; + i_mm->t->semid = semid; + i_mm->t->shmid = shmid; + if (i_mm->t->flag & MM_TMP) { + i_mm->t->template = ALLOC_N(char, strlen(template) + 1); + strcpy(i_mm->t->template, template); + } + } +#endif + } + init = 0; + if (anonymous) { + if (size <= 0) { + rb_raise(rb_eArgError, "length not specified for an anonymous map"); + } + if (offset) { + rb_warning("Ignoring offset for an anonymous map"); + offset = 0; + } + smode = O_RDWR; + pmode = PROT_READ | PROT_WRITE; + i_mm->t->flag |= MM_FIXED | MM_ANON; + } + else { + if (size == 0 && (smode & O_RDWR)) { + if (lseek(fd, i_mm->t->incr - 1, SEEK_END) == -1) { + rb_raise(rb_eIOError, "Can't lseek %d", i_mm->t->incr - 1); + } + if (write(fd, "\000", 1) != 1) { + rb_raise(rb_eIOError, "Can't extend %s", path); + } + init = 1; + size = i_mm->t->incr; + } + if (!NIL_P(fdv)) { + i_mm->t->flag |= MM_FIXED; + } + } + addr = mmap(0, size, pmode, vscope, fd, offset); + if (NIL_P(fdv) && !anonymous) { + close(fd); + } + if (addr == MAP_FAILED || !addr) { + rb_raise(rb_eArgError, "mmap failed (%d)", errno); + } +#ifdef MADV_NORMAL + if (i_mm->t->advice && madvise(addr, size, i_mm->t->advice) == -1) { + rb_raise(rb_eArgError, "madvise(%d)", errno); + } +#endif + if (anonymous && TYPE(options) == T_HASH) { + VALUE val; + char *ptr; + + val = rb_hash_aref(options, rb_str_new2("initialize")); + if (!NIL_P(val)) { + ptr = StringValuePtr(val); + memset(addr, ptr[0], size); + } + } + i_mm->t->addr = addr; + i_mm->t->len = size; + if (!init) i_mm->t->real = size; + i_mm->t->pmode = pmode; + i_mm->t->vscope = vscope; + i_mm->t->smode = smode & ~O_TRUNC; + i_mm->t->path = (path)?ruby_strdup(path):(char *)-1; + if (smode == O_RDONLY) { + obj = rb_obj_freeze(obj); + i_mm->t->flag |= MM_FROZEN; + } + else { + if (smode == O_WRONLY) { + i_mm->t->flag |= MM_FIXED; + } + OBJ_TAINT(obj); + } + return obj; +} + +static VALUE +mm_msync(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + mm_ipc *i_mm; + VALUE oflag; + int ret; + int flag = MS_SYNC; + + if (argc) { + rb_scan_args(argc, argv, "01", &oflag); + flag = NUM2INT(oflag); + } + GetMmap(obj, i_mm, MM_MODIFY); + if ((ret = msync(i_mm->t->addr, i_mm->t->len, flag)) != 0) { + rb_raise(rb_eArgError, "msync(%d)", ret); + } + if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE) + mm_expandf(i_mm, i_mm->t->real); + return obj; +} + +static VALUE +mm_mprotect(obj, a) + VALUE obj, a; +{ + mm_ipc *i_mm; + int ret, pmode; + char *smode; + + GetMmap(obj, i_mm, 0); + if (TYPE(a) == T_STRING) { + smode = StringValuePtr(a); + if (strcmp(smode, "r") == 0) pmode = PROT_READ; + else if (strcmp(smode, "w") == 0) pmode = PROT_WRITE; + else if (strcmp(smode, "rw") == 0 || strcmp(smode, "wr") == 0) + pmode = PROT_READ | PROT_WRITE; + else { + rb_raise(rb_eArgError, "Invalid mode %s", smode); + } + } + else { + pmode = NUM2INT(a); + } + if ((pmode & PROT_WRITE) && (i_mm->t->flag & MM_FROZEN)) + rb_error_frozen("mmap"); + if ((ret = mprotect(i_mm->t->addr, i_mm->t->len, pmode | PROT_READ)) != 0) { + rb_raise(rb_eArgError, "mprotect(%d)", ret); + } + i_mm->t->pmode = pmode; + if (pmode & PROT_READ) { + if (pmode & PROT_WRITE) i_mm->t->smode = O_RDWR; + else { + i_mm->t->smode = O_RDONLY; + obj = rb_obj_freeze(obj); + i_mm->t->flag |= MM_FROZEN; + } + } + else if (pmode & PROT_WRITE) { + i_mm->t->flag |= MM_FIXED; + i_mm->t->smode = O_WRONLY; + } + return obj; +} + +#ifdef MADV_NORMAL +static VALUE +mm_madvise(obj, a) + VALUE obj, a; +{ + mm_ipc *i_mm; + + GetMmap(obj, i_mm, 0); + if (madvise(i_mm->t->addr, i_mm->t->len, NUM2INT(a)) == -1) { + rb_raise(rb_eTypeError, "madvise(%d)", errno); + } + i_mm->t->advice = NUM2INT(a); + return Qnil; +} +#endif + +#define StringMmap(b, bp, bl) \ +do { \ + if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \ + mm_ipc *b_mm; \ + GetMmap(b, b_mm, 0); \ + bp = b_mm->t->addr; \ + bl = b_mm->t->real; \ + } \ + else { \ + bp = StringValuePtr(b); \ + bl = RSTRING(b)->len; \ + } \ +} while (0); + +static void +mm_update(str, beg, len, val) + mm_ipc *str; + VALUE val; + long beg; + long len; +{ + char *valp; + long vall; + + if (str->t->flag & MM_FROZEN) rb_error_frozen("mmap"); + if (len < 0) rb_raise(rb_eIndexError, "negative length %d", len); + mm_lock(str); + if (beg < 0) { + beg += str->t->real; + } + if (beg < 0 || str->t->real < (size_t)beg) { + if (beg < 0) { + beg -= str->t->real; + } + mm_unlock(str); + rb_raise(rb_eIndexError, "index %d out of string", beg); + } + if (str->t->real < (size_t)(beg + len)) { + len = str->t->real - beg; + } + + mm_unlock(str); + StringMmap(val, valp, vall); + mm_lock(str); + + if ((str->t->flag & MM_FIXED) && vall != len) { + mm_unlock(str); + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + if (len < vall) { + mm_realloc(str, str->t->real + vall - len); + } + + if (vall != len) { + memmove((char *)str->t->addr + beg + vall, + (char *)str->t->addr + beg + len, + str->t->real - (beg + len)); + } + if (str->t->real < (size_t)beg && len < 0) { + MEMZERO(str->t->addr + str->t->real, char, -len); + } + if (vall > 0) { + memmove((char *)str->t->addr + beg, valp, vall); + } + str->t->real += vall - len; + mm_unlock(str); +} + +static VALUE +mm_match(x, y) + VALUE x, y; +{ + VALUE reg, res; + long start; + + x = mm_str(x, MM_ORIGIN); + if (TYPE(y) == T_DATA && RDATA(y)->dfree == (RUBY_DATA_FUNC)mm_free) { + y = mm_to_str(y); + } + switch (TYPE(y)) { + case T_REGEXP: + res = rb_reg_match(y, x); + break; + + case T_STRING: + reg = rb_reg_regcomp(y); + start = rb_reg_search(reg, x, 0, 0); + if (start == -1) res = Qnil; + else res = INT2NUM(start); + break; + + default: + res = rb_funcall(y, rb_intern("=~"), 1, x); + break; + } + return res; +} + +static VALUE +get_pat(pat) + VALUE pat; +{ + switch (TYPE(pat)) { + case T_REGEXP: + break; + + case T_STRING: + pat = rb_reg_regcomp(pat); + break; + + default: + /* type failed */ + Check_Type(pat, T_REGEXP); + } + return pat; +} + +static int +mm_correct_backref() +{ + VALUE match; + int i, start; + + match = rb_backref_get(); + if (NIL_P(match)) return 0; + if (RMATCH(match)->BEG(0) == -1) return 0; + start = RMATCH(match)->BEG(0); + RMATCH(match)->str = rb_str_new(StringValuePtr(RMATCH(match)->str) + start, + RMATCH(match)->END(0) - start); + if (OBJ_TAINTED(match)) OBJ_TAINT(RMATCH(match)->str); + for (i = 0; i < RMATCH(match)->regs->num_regs && RMATCH(match)->BEG(i) != -1; i++) { + RMATCH(match)->BEG(i) -= start; + RMATCH(match)->END(i) -= start; + } + rb_backref_set(match); + return start; +} + +static VALUE +mm_sub_bang_int(bang_st) + mm_bang *bang_st; +{ + int argc = bang_st->argc; + VALUE *argv = bang_st->argv; + VALUE obj = bang_st->obj; + VALUE pat, repl = Qnil, match, str, res; + struct re_registers *regs; + int start, iter = 0; + int tainted = 0; + long plen; + mm_ipc *i_mm; + + if (argc == 1 && rb_block_given_p()) { + iter = 1; + } + else if (argc == 2) { + repl = rb_str_to_str(argv[1]); + if (OBJ_TAINTED(repl)) tainted = 1; + } + else { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); + } + GetMmap(obj, i_mm, MM_MODIFY); + str = mm_str(obj, MM_MODIFY | MM_ORIGIN); + + pat = get_pat(argv[0]); + res = Qnil; + if (rb_reg_search(pat, str, 0, 0) >= 0) { + start = mm_correct_backref(); + match = rb_backref_get(); + regs = RMATCH(match)->regs; + if (iter) { + rb_match_busy(match); + repl = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match))); + rb_backref_set(match); + } + else { + RSTRING(str)->ptr += start; + repl = rb_reg_regsub(repl, str, regs); + RSTRING(str)->ptr -= start; + } + if (OBJ_TAINTED(repl)) tainted = 1; + plen = END(0) - BEG(0); + if (RSTRING(repl)->len > plen) { + mm_realloc(i_mm, RSTRING(str)->len + RSTRING(repl)->len - plen); + RSTRING(str)->ptr = i_mm->t->addr; + } + if (RSTRING(repl)->len != plen) { + if (i_mm->t->flag & MM_FIXED) { + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + memmove(RSTRING(str)->ptr + start + BEG(0) + RSTRING(repl)->len, + RSTRING(str)->ptr + start + BEG(0) + plen, + RSTRING(str)->len - start - BEG(0) - plen); + } + memcpy(RSTRING(str)->ptr + start + BEG(0), + RSTRING(repl)->ptr, RSTRING(repl)->len); + i_mm->t->real += RSTRING(repl)->len - plen; + if (tainted) OBJ_TAINT(obj); + + res = obj; + } + rb_gc_force_recycle(str); + return res; +} + +static VALUE +mm_sub_bang(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE res; + mm_bang bang_st; + mm_ipc *i_mm; + + bang_st.argc = argc; + bang_st.argv = argv; + bang_st.obj = obj; + GetMmap(obj, i_mm, MM_MODIFY); + if (i_mm->t->flag & MM_IPC) { + mm_lock(i_mm, Qtrue); + res = rb_ensure(mm_sub_bang_int, (VALUE)&bang_st, mm_vunlock, obj); + } + else { + res = mm_sub_bang_int(&bang_st); + } + return res; +} + +static VALUE +mm_gsub_bang_int(bang_st) + mm_bang *bang_st; +{ + int argc = bang_st->argc; + VALUE *argv = bang_st->argv; + VALUE obj = bang_st->obj; + VALUE pat, val, repl = Qnil, match, str; + struct re_registers *regs; + long beg, offset; + int start, iter = 0; + int tainted = 0; + long plen; + mm_ipc *i_mm; + + if (argc == 1 && rb_block_given_p()) { + iter = 1; + } + else if (argc == 2) { + repl = rb_str_to_str(argv[1]); + if (OBJ_TAINTED(repl)) tainted = 1; + } + else { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); + } + GetMmap(obj, i_mm, MM_MODIFY); + str = mm_str(obj, MM_MODIFY | MM_ORIGIN); + + pat = get_pat(argv[0]); + offset = 0; + beg = rb_reg_search(pat, str, 0, 0); + if (beg < 0) { + rb_gc_force_recycle(str); + return Qnil; + } + while (beg >= 0) { + start = mm_correct_backref(); + match = rb_backref_get(); + regs = RMATCH(match)->regs; + if (iter) { + rb_match_busy(match); + val = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match))); + rb_backref_set(match); + } + else { + RSTRING(str)->ptr += start; + val = rb_reg_regsub(repl, str, regs); + RSTRING(str)->ptr -= start; + } + if (OBJ_TAINTED(repl)) tainted = 1; + plen = END(0) - BEG(0); + if ((i_mm->t->real + RSTRING(val)->len - plen) > i_mm->t->len) { + mm_realloc(i_mm, RSTRING(str)->len + RSTRING(val)->len - plen); + } + if (RSTRING(val)->len != plen) { + if (i_mm->t->flag & MM_FIXED) { + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + memmove(RSTRING(str)->ptr + start + BEG(0) + RSTRING(val)->len, + RSTRING(str)->ptr + start + BEG(0) + plen, + RSTRING(str)->len - start - BEG(0) - plen); + } + memcpy(RSTRING(str)->ptr + start + BEG(0), + RSTRING(val)->ptr, RSTRING(val)->len); + RSTRING(str)->len += RSTRING(val)->len - plen; + i_mm->t->real = RSTRING(str)->len; + if (BEG(0) == END(0)) { + offset = start + END(0) + mbclen2(RSTRING(str)->ptr[END(0)], pat); + offset += RSTRING(val)->len - plen; + } + else { + offset = start + END(0) + RSTRING(val)->len - plen; + } + if (offset > RSTRING(str)->len) break; + beg = rb_reg_search(pat, str, offset, 0); + } + rb_backref_set(match); + if (tainted) OBJ_TAINT(obj); + rb_gc_force_recycle(str); + return obj; +} + +static VALUE +mm_gsub_bang(argc, argv, obj) + int argc; + VALUE *argv; + VALUE obj; +{ + VALUE res; + mm_bang bang_st; + mm_ipc *i_mm; + + bang_st.argc = argc; + bang_st.argv = argv; + bang_st.obj = obj; + GetMmap(obj, i_mm, MM_MODIFY); + if (i_mm->t->flag & MM_IPC) { + mm_lock(i_mm, Qtrue); + res = rb_ensure(mm_gsub_bang_int, (VALUE)&bang_st, mm_vunlock, obj); + } + else { + res = mm_gsub_bang_int(&bang_st); + } + return res; +} + +static VALUE mm_index __((int, VALUE *, VALUE)); + +#if HAVE_RB_DEFINE_ALLOC_FUNC + +static void +mm_subpat_set(obj, re, offset, val) + VALUE obj, re; + int offset; + VALUE val; +{ + VALUE str, match; + int start, end, len; + mm_ipc *i_mm; + + str = mm_str(obj, MM_MODIFY | MM_ORIGIN); + if (rb_reg_search(re, str, 0, 0) < 0) { + rb_raise(rb_eIndexError, "regexp not matched"); + } + match = rb_backref_get(); + if (offset >= RMATCH(match)->regs->num_regs) { + rb_raise(rb_eIndexError, "index %d out of regexp", offset); + } + + start = RMATCH(match)->BEG(offset); + if (start == -1) { + rb_raise(rb_eIndexError, "regexp group %d not matched", offset); + } + end = RMATCH(match)->END(offset); + len = end - start; + GetMmap(obj, i_mm, MM_MODIFY); + mm_update(i_mm, start, len, val); +} + +#endif + +static VALUE +mm_aset(str, indx, val) + VALUE str; + VALUE indx, val; +{ + long idx; + mm_ipc *i_mm; + + GetMmap(str, i_mm, MM_MODIFY); + switch (TYPE(indx)) { + case T_FIXNUM: + num_index: + idx = NUM2INT(indx); + if (idx < 0) { + idx += i_mm->t->real; + } + if (idx < 0 || i_mm->t->real <= (size_t)idx) { + rb_raise(rb_eIndexError, "index %d out of string", idx); + } + if (FIXNUM_P(val)) { + if (i_mm->t->real == (size_t)idx) { + i_mm->t->real += 1; + mm_realloc(i_mm, i_mm->t->real); + } + ((char *)i_mm->t->addr)[idx] = NUM2INT(val) & 0xff; + } + else { + mm_update(i_mm, idx, 1, val); + } + return val; + + case T_REGEXP: +#if HAVE_RB_DEFINE_ALLOC_FUNC + mm_subpat_set(str, indx, 0, val); +#else + { + VALUE args[2]; + args[0] = indx; + args[1] = val; + mm_sub_bang(2, args, str); + } +#endif + return val; + + case T_STRING: + { + VALUE res; + + res = mm_index(1, &indx, str); + if (!NIL_P(res)) { + mm_update(i_mm, NUM2LONG(res), RSTRING(indx)->len, val); + } + return val; + } + + default: + /* check if indx is Range */ + { + long beg, len; + if (rb_range_beg_len(indx, &beg, &len, i_mm->t->real, 2)) { + mm_update(i_mm, beg, len, val); + return val; + } + } + idx = NUM2LONG(indx); + goto num_index; + } +} + +static VALUE +mm_aset_m(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + mm_ipc *i_mm; + + GetMmap(str, i_mm, MM_MODIFY); + if (argc == 3) { + long beg, len; + +#if HAVE_RB_DEFINE_ALLOC_FUNC + if (TYPE(argv[0]) == T_REGEXP) { + mm_subpat_set(str, argv[0], NUM2INT(argv[1]), argv[2]); + } + else +#endif + { + beg = NUM2INT(argv[0]); + len = NUM2INT(argv[1]); + mm_update(i_mm, beg, len, argv[2]); + } + return argv[2]; + } + if (argc != 2) { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); + } + return mm_aset(str, argv[0], argv[1]); +} + +#if HAVE_RB_STR_INSERT + +static VALUE +mm_insert(str, idx, str2) + VALUE str, idx, str2; +{ + mm_ipc *i_mm; + long pos = NUM2LONG(idx); + + GetMmap(str, i_mm, MM_MODIFY); + if (pos == -1) { + pos = RSTRING(str)->len; + } + else if (pos < 0) { + pos++; + } + mm_update(i_mm, pos, 0, str2); + return str; +} + +#endif + +static VALUE mm_aref_m _((int, VALUE *, VALUE)); + +static VALUE +mm_slice_bang(argc, argv, str) + int argc; + VALUE *argv; + VALUE str; +{ + VALUE result; + VALUE buf[3]; + int i; + + if (argc < 1 || 2 < argc) { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); + } + for (i = 0; i < argc; i++) { + buf[i] = argv[i]; + } + buf[i] = rb_str_new(0,0); + result = mm_aref_m(argc, buf, str); + if (!NIL_P(result)) { + mm_aset_m(argc+1, buf, str); + } + return result; +} + +static VALUE +mm_cat(str, ptr, len) + VALUE str; + const char *ptr; + long len; +{ + mm_ipc *i_mm; + char *sptr; + + GetMmap(str, i_mm, MM_MODIFY); + if (len > 0) { + int poffset = -1; + sptr = (char *)i_mm->t->addr; + + if (sptr <= ptr && + ptr < sptr + i_mm->t->real) { + poffset = ptr - sptr; + } + mm_lock(i_mm, Qtrue); + mm_realloc(i_mm, i_mm->t->real + len); + sptr = (char *)i_mm->t->addr; + if (ptr) { + if (poffset >= 0) ptr = sptr + poffset; + memcpy(sptr + i_mm->t->real, ptr, len); + } + i_mm->t->real += len; + mm_unlock(i_mm); + } + return str; +} + +static VALUE +mm_append(str1, str2) + VALUE str1, str2; +{ + str2 = rb_str_to_str(str2); + str1 = mm_cat(str1, StringValuePtr(str2), RSTRING(str2)->len); + return str1; +} + +static VALUE +mm_concat(str1, str2) + VALUE str1, str2; +{ + if (FIXNUM_P(str2)) { + int i = FIX2INT(str2); + if (0 <= i && i <= 0xff) { /* byte */ + char c = i; + return mm_cat(str1, &c, 1); + } + } + str1 = mm_append(str1, str2); + return str1; +} + +#ifndef HAVE_RB_STR_LSTRIP + +static VALUE +mm_strip_bang(str) + VALUE str; +{ + char *s, *t, *e; + mm_ipc *i_mm; + + GetMmap(str, i_mm, MM_MODIFY); + mm_lock(i_mm, Qtrue); + s = (char *)i_mm->t->addr; + e = t = s + i_mm->t->real; + while (s < t && ISSPACE(*s)) s++; + t--; + while (s <= t && ISSPACE(*t)) t--; + t++; + + if (i_mm->t->real != (t - s) && (i_mm->t->flag & MM_FIXED)) { + mm_unlock(i_mm); + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + i_mm->t->real = t-s; + if (s > (char *)i_mm->t->addr) { + memmove(i_mm->t->addr, s, i_mm->t->real); + ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; + } + else if (t < e) { + ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; + } + else { + str = Qnil; + } + mm_unlock(i_mm); + return str; +} + +#else + +static VALUE +mm_lstrip_bang(str) + VALUE str; +{ + char *s, *t, *e; + mm_ipc *i_mm; + + GetMmap(str, i_mm, MM_MODIFY); + mm_lock(i_mm, Qtrue); + s = (char *)i_mm->t->addr; + e = t = s + i_mm->t->real; + while (s < t && ISSPACE(*s)) s++; + + if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) { + mm_unlock(i_mm); + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + i_mm->t->real = t - s; + if (s > (char *)i_mm->t->addr) { + memmove(i_mm->t->addr, s, i_mm->t->real); + ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; + mm_unlock(i_mm); + return str; + } + mm_unlock(i_mm); + return Qnil; +} + +static VALUE +mm_rstrip_bang(str) + VALUE str; +{ + char *s, *t, *e; + mm_ipc *i_mm; + + GetMmap(str, i_mm, MM_MODIFY); + mm_lock(i_mm, Qtrue); + s = (char *)i_mm->t->addr; + e = t = s + i_mm->t->real; + t--; + while (s <= t && ISSPACE(*t)) t--; + t++; + if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) { + mm_unlock(i_mm); + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + i_mm->t->real = t - s; + if (t < e) { + ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; + mm_unlock(i_mm); + return str; + } + mm_unlock(i_mm); + return Qnil; +} + +static VALUE +mm_strip_bang(str) + VALUE str; +{ + VALUE l = mm_lstrip_bang(str); + VALUE r = mm_rstrip_bang(str); + + if (NIL_P(l) && NIL_P(r)) return Qnil; + return str; +} + +#endif + +#define MmapStr(b, recycle) \ +do { \ + recycle = 0; \ + if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \ + recycle = 1; \ + b = mm_str(b, MM_ORIGIN); \ + } \ + else { \ + b = rb_str_to_str(b); \ + } \ +} while (0); + + +static VALUE +mm_cmp(a, b) + VALUE a, b; +{ + int result; + int recycle = 0; + + a = mm_str(a, MM_ORIGIN); + MmapStr(b, recycle); + result = rb_str_cmp(a, b); + rb_gc_force_recycle(a); + if (recycle) rb_gc_force_recycle(b); + return INT2FIX(result); +} + +#if HAVE_RB_STR_CASECMP + +static VALUE +mm_casecmp(a, b) + VALUE a, b; +{ + VALUE result; + int recycle = 0; + + a = mm_str(a, MM_ORIGIN); + MmapStr(b, recycle); + result = rb_funcall2(a, rb_intern("casecmp"), 1, &b); + rb_gc_force_recycle(a); + if (recycle) rb_gc_force_recycle(b); + return result; +} + +#endif + +static VALUE +mm_equal(a, b) + VALUE a, b; +{ + VALUE result; + mm_ipc *i_mm, *u_mm; + + if (a == b) return Qtrue; + if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free) + return Qfalse; + + GetMmap(a, i_mm, 0); + GetMmap(b, u_mm, 0); + if (i_mm->t->real != u_mm->t->real) + return Qfalse; + a = mm_str(a, MM_ORIGIN); + b = mm_str(b, MM_ORIGIN); + result = rb_funcall2(a, rb_intern("=="), 1, &b); + rb_gc_force_recycle(a); + rb_gc_force_recycle(b); + return result; +} + +static VALUE +mm_eql(a, b) + VALUE a, b; +{ + VALUE result; + mm_ipc *i_mm, *u_mm; + + if (a == b) return Qtrue; + if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free) + return Qfalse; + + GetMmap(a, i_mm, 0); + GetMmap(b, u_mm, 0); + if (i_mm->t->real != u_mm->t->real) + return Qfalse; + a = mm_str(a, MM_ORIGIN); + b = mm_str(b, MM_ORIGIN); + result = rb_funcall2(a, rb_intern("eql?"), 1, &b); + rb_gc_force_recycle(a); + rb_gc_force_recycle(b); + return result; +} + +static VALUE +mm_hash(a) + VALUE a; +{ + VALUE b; + int res; + + b = mm_str(a, MM_ORIGIN); + res = rb_str_hash(b); + rb_gc_force_recycle(b); + return INT2FIX(res); +} + +static VALUE +mm_size(a) + VALUE a; +{ + mm_ipc *i_mm; + + GetMmap(a, i_mm, 0); + return UINT2NUM(i_mm->t->real); +} + +static VALUE +mm_empty(a) + VALUE a; +{ + mm_ipc *i_mm; + + GetMmap(a, i_mm, 0); + if (i_mm->t->real == 0) return Qtrue; + return Qfalse; +} + +static VALUE +mm_protect_bang(t) + VALUE *t; +{ + return rb_funcall2(t[0], (ID)t[1], (int)t[2], (VALUE *)t[3]); +} + +static VALUE +mm_recycle(str) + VALUE str; +{ + rb_gc_force_recycle(str); + return str; +} + +static VALUE +mm_i_bang(bang_st) + mm_bang *bang_st; +{ + VALUE str, res; + mm_ipc *i_mm; + + str = mm_str(bang_st->obj, bang_st->flag); + if (bang_st->flag & MM_PROTECT) { + VALUE tmp[4]; + tmp[0] = str; + tmp[1] = (VALUE)bang_st->id; + tmp[2] = (VALUE)bang_st->argc; + tmp[3] = (VALUE)bang_st->argv; + res = rb_ensure(mm_protect_bang, (VALUE)tmp, mm_recycle, str); + } + else { + res = rb_funcall2(str, bang_st->id, bang_st->argc, bang_st->argv); + rb_gc_force_recycle(str); + } + if (res != Qnil) { + GetMmap(bang_st->obj, i_mm, 0); + i_mm->t->real = RSTRING(str)->len; + } + return res; +} + + +static VALUE +mm_bang_i(obj, flag, id, argc, argv) + VALUE obj, *argv; + int flag, id, argc; +{ + VALUE res; + mm_ipc *i_mm; + mm_bang bang_st; + + GetMmap(obj, i_mm, 0); + if ((flag & MM_CHANGE) && (i_mm->t->flag & MM_FIXED)) { + rb_raise(rb_eTypeError, "try to change the size of a fixed map"); + } + bang_st.obj = obj; + bang_st.flag = flag; + bang_st.id = id; + bang_st.argc = argc; + bang_st.argv = argv; + if (i_mm->t->flag & MM_IPC) { + mm_lock(i_mm, Qtrue); + res = rb_ensure(mm_i_bang, (VALUE)&bang_st, mm_vunlock, obj); + } + else { + res = mm_i_bang(&bang_st); + } + if (res == Qnil) return res; + return (flag & MM_ORIGIN)?res:obj; + +} + +#if HAVE_RB_STR_MATCH + +static VALUE +mm_match_m(a, b) + VALUE a, b; +{ + return mm_bang_i(a, MM_ORIGIN, rb_intern("match"), 1, &b); +} + +#endif + +static VALUE +mm_upcase_bang(a) + VALUE a; +{ + return mm_bang_i(a, MM_MODIFY, rb_intern("upcase!"), 0, 0); +} + +static VALUE +mm_downcase_bang(a) + VALUE a; +{ + return mm_bang_i(a, MM_MODIFY, rb_intern("downcase!"), 0, 0); +} + +static VALUE +mm_capitalize_bang(a) + VALUE a; +{ + return mm_bang_i(a, MM_MODIFY, rb_intern("capitalize!"), 0, 0); +} + +static VALUE +mm_swapcase_bang(a) + VALUE a; +{ + return mm_bang_i(a, MM_MODIFY, rb_intern("swapcase!"), 0, 0); +} + +static VALUE +mm_reverse_bang(a) + VALUE a; +{ + return mm_bang_i(a, MM_MODIFY, rb_intern("reverse!"), 0, 0); +} + +static VALUE +mm_chop_bang(a) + VALUE a; +{ + return mm_bang_i(a, MM_CHANGE, rb_intern("chop!"), 0, 0); +} + +static VALUE +mm_inspect(a) + VALUE a; +{ + return rb_any_to_s(a); +} + +static VALUE +mm_chomp_bang(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("chomp!"), argc, argv); +} + +static VALUE +mm_delete_bang(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("delete!"), argc, argv); +} + +static VALUE +mm_squeeze_bang(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("squeeze!"), argc, argv); +} + +static VALUE +mm_tr_bang(obj, a, b) + VALUE obj, a, b; +{ + VALUE tmp[2]; + tmp[0] = a; + tmp[1] = b; + return mm_bang_i(obj, MM_MODIFY | MM_PROTECT, rb_intern("tr!"), 2, tmp); +} + +static VALUE +mm_tr_s_bang(obj, a, b) + VALUE obj, a, b; +{ + VALUE tmp[2]; + tmp[0] = a; + tmp[1] = b; + return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("tr_s!"), 2, tmp); +} + +static VALUE +mm_crypt(a, b) + VALUE a, b; +{ + return mm_bang_i(a, MM_ORIGIN, rb_intern("crypt"), 1, &b); +} + +static VALUE +mm_include(a, b) + VALUE a, b; +{ + return mm_bang_i(a, MM_ORIGIN, rb_intern("include?"), 1, &b); +} + +static VALUE +mm_index(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_ORIGIN, rb_intern("index"), argc, argv); +} + +static VALUE +mm_rindex(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_ORIGIN, rb_intern("rindex"), argc, argv); +} + +static VALUE +mm_aref_m(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_ORIGIN, rb_intern("[]"), argc, argv); +} + +static VALUE +mm_sum(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_ORIGIN, rb_intern("sum"), argc, argv); +} + +static VALUE +mm_split(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_ORIGIN, rb_intern("split"), argc, argv); +} + +static VALUE +mm_count(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + return mm_bang_i(obj, MM_ORIGIN, rb_intern("count"), argc, argv); +} + +static VALUE +mm_internal_each(tmp) + VALUE *tmp; +{ + return rb_funcall2(tmp[0], (ID)tmp[1], (int)tmp[2], (VALUE *)tmp[3]); +} + +static VALUE +mm_scan(obj, a) + VALUE obj, a; +{ + VALUE tmp[4]; + + if (!rb_block_given_p()) { + return rb_funcall(mm_str(obj, MM_ORIGIN), rb_intern("scan"), 1, a); + } + tmp[0] = mm_str(obj, MM_ORIGIN); + tmp[1] = (VALUE)rb_intern("scan"); + tmp[2] = (VALUE)1; + tmp[3] = (VALUE)&a; + rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); + return obj; +} + +static VALUE +mm_each_line(argc, argv, obj) + int argc; + VALUE obj, *argv; +{ + VALUE tmp[4]; + + tmp[0] = mm_str(obj, MM_ORIGIN); + tmp[1] = (VALUE)rb_intern("each_line"); + tmp[2] = (VALUE)argc; + tmp[3] = (VALUE)argv; + rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); + return obj; +} + +static VALUE +mm_each_byte(argc, argv, obj) + int argc; + VALUE obj, *argv; +{ + VALUE tmp[4]; + + tmp[0] = mm_str(obj, MM_ORIGIN); + tmp[1] = (VALUE)rb_intern("each_byte"); + tmp[2] = (VALUE)argc; + tmp[3] = (VALUE)argv; + rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); + return obj; +} + +static VALUE +mm_undefined(argc, argv, obj) + int argc; + VALUE *argv, obj; +{ + rb_raise(rb_eNameError, "not yet implemented"); +} + +static VALUE +mm_mlockall(obj, flag) + VALUE obj, flag; +{ + if (mlockall(NUM2INT(flag)) == -1) { + rb_raise(rb_eArgError, "mlockall(%d)", errno); + } + return Qnil; +} + +static VALUE +mm_munlockall(obj) + VALUE obj; +{ + if (munlockall() == -1) { + rb_raise(rb_eArgError, "munlockall(%d)", errno); + } + return Qnil; +} + +static VALUE +mm_mlock(obj) + VALUE obj; +{ + mm_ipc *i_mm; + + Data_Get_Struct(obj, mm_ipc, i_mm); + if (i_mm->t->flag & MM_LOCK) { + return obj; + } + if (i_mm->t->flag & MM_ANON) { + rb_raise(rb_eArgError, "mlock(anonymous)"); + } + if (mlock(i_mm->t->addr, i_mm->t->len) == -1) { + rb_raise(rb_eArgError, "mlock(%d)", errno); + } + i_mm->t->flag |= MM_LOCK; + return obj; +} + +static VALUE +mm_munlock(obj) + VALUE obj; +{ + mm_ipc *i_mm; + + Data_Get_Struct(obj, mm_ipc, i_mm); + if (!(i_mm->t->flag & MM_LOCK)) { + return obj; + } + if (munlock(i_mm->t->addr, i_mm->t->len) == -1) { + rb_raise(rb_eArgError, "munlock(%d)", errno); + } + i_mm->t->flag &= ~MM_LOCK; + return obj; +} + +void +Init_mmap() +{ + if (rb_const_defined_at(rb_cObject, rb_intern("Mmap"))) { + rb_raise(rb_eNameError, "class already defined"); + } + mm_cMap = rb_define_class("Mmap", rb_cObject); + rb_define_const(mm_cMap, "MS_SYNC", INT2FIX(MS_SYNC)); + rb_define_const(mm_cMap, "MS_ASYNC", INT2FIX(MS_ASYNC)); + rb_define_const(mm_cMap, "MS_INVALIDATE", INT2FIX(MS_INVALIDATE)); + rb_define_const(mm_cMap, "PROT_READ", INT2FIX(PROT_READ)); + rb_define_const(mm_cMap, "PROT_WRITE", INT2FIX(PROT_WRITE)); + rb_define_const(mm_cMap, "PROT_EXEC", INT2FIX(PROT_EXEC)); + rb_define_const(mm_cMap, "PROT_NONE", INT2FIX(PROT_NONE)); + rb_define_const(mm_cMap, "MAP_SHARED", INT2FIX(MAP_SHARED)); + rb_define_const(mm_cMap, "MAP_PRIVATE", INT2FIX(MAP_PRIVATE)); +#ifdef MADV_NORMAL + rb_define_const(mm_cMap, "MADV_NORMAL", INT2FIX(MADV_NORMAL)); + rb_define_const(mm_cMap, "MADV_RANDOM", INT2FIX(MADV_RANDOM)); + rb_define_const(mm_cMap, "MADV_SEQUENTIAL", INT2FIX(MADV_SEQUENTIAL)); + rb_define_const(mm_cMap, "MADV_WILLNEED", INT2FIX(MADV_WILLNEED)); + rb_define_const(mm_cMap, "MADV_DONTNEED", INT2FIX(MADV_DONTNEED)); +#endif +#ifdef MAP_DENYWRITE + rb_define_const(mm_cMap, "MAP_DENYWRITE", INT2FIX(MAP_DENYWRITE)); +#endif +#ifdef MAP_EXECUTABLE + rb_define_const(mm_cMap, "MAP_EXECUTABLE", INT2FIX(MAP_EXECUTABLE)); +#endif +#ifdef MAP_NORESERVE + rb_define_const(mm_cMap, "MAP_NORESERVE", INT2FIX(MAP_NORESERVE)); +#endif +#ifdef MAP_LOCKED + rb_define_const(mm_cMap, "MAP_LOCKED", INT2FIX(MAP_LOCKED)); +#endif +#ifdef MAP_GROWSDOWN + rb_define_const(mm_cMap, "MAP_GROWSDOWN", INT2FIX(MAP_GROWSDOWN)); +#endif +#ifdef MAP_ANON + rb_define_const(mm_cMap, "MAP_ANON", INT2FIX(MAP_ANON)); +#endif +#ifdef MAP_ANONYMOUS + rb_define_const(mm_cMap, "MAP_ANONYMOUS", INT2FIX(MAP_ANONYMOUS)); +#endif +#ifdef MAP_NOSYNC + rb_define_const(mm_cMap, "MAP_NOSYNC", INT2FIX(MAP_NOSYNC)); +#endif +#ifdef MCL_CURRENT + rb_define_const(mm_cMap, "MCL_CURRENT", INT2FIX(MCL_CURRENT)); + rb_define_const(mm_cMap, "MCL_FUTURE", INT2FIX(MCL_FUTURE)); +#endif + rb_include_module(mm_cMap, rb_mComparable); + rb_include_module(mm_cMap, rb_mEnumerable); + +#if HAVE_RB_DEFINE_ALLOC_FUNC + rb_define_alloc_func(mm_cMap, mm_s_alloc); +#else + rb_define_singleton_method(mm_cMap, "allocate", mm_s_alloc, 0); +#endif + rb_define_singleton_method(mm_cMap, "new", mm_s_new, -1); + rb_define_singleton_method(mm_cMap, "mlockall", mm_mlockall, 1); + rb_define_singleton_method(mm_cMap, "lockall", mm_mlockall, 1); + rb_define_singleton_method(mm_cMap, "munlockall", mm_munlockall, 0); + rb_define_singleton_method(mm_cMap, "unlockall", mm_munlockall, 0); + + rb_define_method(mm_cMap, "initialize", mm_init, -1); + + rb_define_method(mm_cMap, "unmap", mm_unmap, 0); + rb_define_method(mm_cMap, "munmap", mm_unmap, 0); + rb_define_method(mm_cMap, "msync", mm_msync, -1); + rb_define_method(mm_cMap, "sync", mm_msync, -1); + rb_define_method(mm_cMap, "flush", mm_msync, -1); + rb_define_method(mm_cMap, "mprotect", mm_mprotect, 1); + rb_define_method(mm_cMap, "protect", mm_mprotect, 1); +#ifdef MADV_NORMAL + rb_define_method(mm_cMap, "madvise", mm_madvise, 1); + rb_define_method(mm_cMap, "advise", mm_madvise, 1); +#endif + rb_define_method(mm_cMap, "mlock", mm_mlock, 0); + rb_define_method(mm_cMap, "lock", mm_mlock, 0); + rb_define_method(mm_cMap, "munlock", mm_munlock, 0); + rb_define_method(mm_cMap, "unlock", mm_munlock, 0); + + rb_define_method(mm_cMap, "extend", mm_extend, 1); + rb_define_method(mm_cMap, "freeze", mm_freeze, 0); + rb_define_method(mm_cMap, "clone", mm_undefined, -1); + rb_define_method(mm_cMap, "initialize_copy", mm_undefined, -1); + rb_define_method(mm_cMap, "dup", mm_undefined, -1); + rb_define_method(mm_cMap, "<=>", mm_cmp, 1); + rb_define_method(mm_cMap, "==", mm_equal, 1); + rb_define_method(mm_cMap, "===", mm_equal, 1); + rb_define_method(mm_cMap, "eql?", mm_eql, 1); + rb_define_method(mm_cMap, "hash", mm_hash, 0); +#if HAVE_RB_STR_CASECMP + rb_define_method(mm_cMap, "casecmp", mm_casecmp, 1); +#endif + rb_define_method(mm_cMap, "+", mm_undefined, -1); + rb_define_method(mm_cMap, "*", mm_undefined, -1); + rb_define_method(mm_cMap, "%", mm_undefined, -1); + rb_define_method(mm_cMap, "[]", mm_aref_m, -1); + rb_define_method(mm_cMap, "[]=", mm_aset_m, -1); +#if HAVE_RB_STR_INSERT + rb_define_method(mm_cMap, "insert", mm_insert, 2); +#endif + rb_define_method(mm_cMap, "length", mm_size, 0); + rb_define_method(mm_cMap, "size", mm_size, 0); + rb_define_method(mm_cMap, "empty?", mm_empty, 0); + rb_define_method(mm_cMap, "=~", mm_match, 1); + rb_define_method(mm_cMap, "~", mm_undefined, -1); +#if HAVE_RB_STR_MATCH + rb_define_method(mm_cMap, "match", mm_match_m, 1); +#endif + rb_define_method(mm_cMap, "succ", mm_undefined, -1); + rb_define_method(mm_cMap, "succ!", mm_undefined, -1); + rb_define_method(mm_cMap, "next", mm_undefined, -1); + rb_define_method(mm_cMap, "next!", mm_undefined, -1); + rb_define_method(mm_cMap, "upto", mm_undefined, -1); + rb_define_method(mm_cMap, "index", mm_index, -1); + rb_define_method(mm_cMap, "rindex", mm_rindex, -1); + rb_define_method(mm_cMap, "replace", mm_undefined, -1); + + rb_define_method(mm_cMap, "to_i", mm_undefined, -1); + rb_define_method(mm_cMap, "to_f", mm_undefined, -1); + rb_define_method(mm_cMap, "to_sym", mm_undefined, -1); + rb_define_method(mm_cMap, "to_s", rb_any_to_s, 0); + rb_define_method(mm_cMap, "to_str", mm_to_str, 0); + rb_define_method(mm_cMap, "inspect", mm_inspect, 0); + rb_define_method(mm_cMap, "dump", mm_undefined, -1); + + rb_define_method(mm_cMap, "upcase", mm_undefined, -1); + rb_define_method(mm_cMap, "downcase", mm_undefined, -1); + rb_define_method(mm_cMap, "capitalize", mm_undefined, -1); + rb_define_method(mm_cMap, "swapcase", mm_undefined, -1); + + rb_define_method(mm_cMap, "upcase!", mm_upcase_bang, 0); + rb_define_method(mm_cMap, "downcase!", mm_downcase_bang, 0); + rb_define_method(mm_cMap, "capitalize!", mm_capitalize_bang, 0); + rb_define_method(mm_cMap, "swapcase!", mm_swapcase_bang, 0); + + rb_define_method(mm_cMap, "hex", mm_undefined, -1); + rb_define_method(mm_cMap, "oct", mm_undefined, -1); + rb_define_method(mm_cMap, "split", mm_split, -1); + rb_define_method(mm_cMap, "reverse", mm_undefined, -1); + rb_define_method(mm_cMap, "reverse!", mm_reverse_bang, 0); + rb_define_method(mm_cMap, "concat", mm_concat, 1); + rb_define_method(mm_cMap, "<<", mm_concat, 1); + rb_define_method(mm_cMap, "crypt", mm_crypt, 1); + rb_define_method(mm_cMap, "intern", mm_undefined, -1); + + rb_define_method(mm_cMap, "include?", mm_include, 1); + + rb_define_method(mm_cMap, "scan", mm_scan, 1); + + rb_define_method(mm_cMap, "ljust", mm_undefined, -1); + rb_define_method(mm_cMap, "rjust", mm_undefined, -1); + rb_define_method(mm_cMap, "center", mm_undefined, -1); + + rb_define_method(mm_cMap, "sub", mm_undefined, -1); + rb_define_method(mm_cMap, "gsub", mm_undefined, -1); + rb_define_method(mm_cMap, "chop", mm_undefined, -1); + rb_define_method(mm_cMap, "chomp", mm_undefined, -1); + rb_define_method(mm_cMap, "strip", mm_undefined, -1); +#if HAVE_RB_STR_LSTRIP + rb_define_method(mm_cMap, "lstrip", mm_undefined, -1); + rb_define_method(mm_cMap, "rstrip", mm_undefined, -1); +#endif + + rb_define_method(mm_cMap, "sub!", mm_sub_bang, -1); + rb_define_method(mm_cMap, "gsub!", mm_gsub_bang, -1); + rb_define_method(mm_cMap, "strip!", mm_strip_bang, 0); +#if HAVE_RB_STR_LSTRIP + rb_define_method(mm_cMap, "lstrip!", mm_lstrip_bang, 0); + rb_define_method(mm_cMap, "rstrip!", mm_rstrip_bang, 0); +#endif + rb_define_method(mm_cMap, "chop!", mm_chop_bang, 0); + rb_define_method(mm_cMap, "chomp!", mm_chomp_bang, -1); + + rb_define_method(mm_cMap, "tr", mm_undefined, -1); + rb_define_method(mm_cMap, "tr_s", mm_undefined, -1); + rb_define_method(mm_cMap, "delete", mm_undefined, -1); + rb_define_method(mm_cMap, "squeeze", mm_undefined, -1); + rb_define_method(mm_cMap, "count", mm_count, -1); + + rb_define_method(mm_cMap, "tr!", mm_tr_bang, 2); + rb_define_method(mm_cMap, "tr_s!", mm_tr_s_bang, 2); + rb_define_method(mm_cMap, "delete!", mm_delete_bang, -1); + rb_define_method(mm_cMap, "squeeze!", mm_squeeze_bang, -1); + + rb_define_method(mm_cMap, "each_line", mm_each_line, -1); + rb_define_method(mm_cMap, "each", mm_each_line, -1); + rb_define_method(mm_cMap, "each_byte", mm_each_byte, -1); + + rb_define_method(mm_cMap, "sum", mm_sum, -1); + + rb_define_method(mm_cMap, "slice", mm_aref_m, -1); + rb_define_method(mm_cMap, "slice!", mm_slice_bang, -1); + rb_define_method(mm_cMap, "semlock", mm_semlock, -1); + rb_define_method(mm_cMap, "ipc_key", mm_ipc_key, 0); +} diff --git a/extconf.rb b/extconf.rb deleted file mode 100644 index fc178b5..0000000 --- a/extconf.rb +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/ruby -ARGV.collect! {|x| x.sub(/^--with-mmap-prefix=/, "--with-mmap-dir=") } - -require 'mkmf' - -if unknown = enable_config("unknown") - libs = if CONFIG.key?("LIBRUBYARG_STATIC") - Config::expand(CONFIG["LIBRUBYARG_STATIC"].dup).sub(/^-l/, '') - else - Config::expand(CONFIG["LIBRUBYARG"].dup).sub(/^lib([^.]*).*/, '\\1') - end - unknown = find_library(libs, "ruby_init", - Config::expand(CONFIG["archdir"].dup)) -end - -dir_config("mmap") - -["lstrip", "match", "insert", "casecmp"].each do |func| - if "aa".respond_to?(func) - $CFLAGS += " -DHAVE_RB_STR_#{func.upcase}" - end -end - -if enable_config("ipc") - unless have_func("semctl") && have_func("shmctl") - $stderr.puts "\tIPC will not be available" - end -end - -$CFLAGS += " -DRUBYLIBDIR='\"#{CONFIG['rubylibdir']}\"'" - -create_makefile "mmap" - -begin - make = open("Makefile", "a") - make.puts "\ntest: $(DLLIB)" - Dir.foreach('test') do |x| - next if /^\./ =~ x || /(_\.rb|~)$/ =~ x - next if FileTest.directory?(x) - make.print "\truby test/#{x}\n" - end - if unknown - make.print <<-EOT - -unknown: $(DLLIB) -\t@echo "main() {}" > /tmp/a.c -\t$(CC) -static /tmp/a.c $(OBJS) $(CPPFLAGS) $(DLDFLAGS) $(LIBS) $(LOCAL_LIBS) -\t@-rm /tmp/a.c a.out - -EOT - end - make.print <<-EOT -%.html: %.rd -\trd2 $< > ${<:%.rd=%.html} - - EOT - make.print "HTML = mmap.html" - doc = Dir['doc/*.rd'] - doc.each {|x| make.print " \\\n\t#{x.sub(/\.rd$/, '.html')}" } - make.print "\n\nRDOC = doc/mmap.rb" - make.puts - make.print <<-EOF - -rdoc: doc/doc/index.html - -doc/doc/index.html: $(RDOC) -\t@-(cd doc; rdoc mmap.rb) - -ri: doc/mmap.rb -\t@-(cd doc; rdoc -r mmap.rb) - -ri-site: doc/mmap.rb -\t@-(cd doc; rdoc -R mmap.rb) - -rd2: html - -html: $(HTML) - - EOF -ensure - make.close -end - diff --git a/gem_spec.rb b/gem_spec.rb new file mode 100644 index 0000000..a310bf3 --- /dev/null +++ b/gem_spec.rb @@ -0,0 +1,28 @@ +# GEM SPECIFICATION +GEM_SPEC = Gem::Specification.new { |s| + s.name = UNIX_NAME + s.version = PROJECT_VERSION + s.summary = PROJECT_SUMMARY + s.rubyforge_project = UNIX_NAME + s.homepage = PROJECT_HOMEPAGE + s.author = USER_NAME + s.email = USER_EMAIL + s.files = DIST_FILES + s.test_files = TEST_FILES + s.executables = BIN_FILES.map { |fn| File.basename(fn) } + s.has_rdoc = true + s.extra_rdoc_files = RDOC_FILES + #s.rdoc_options = GENERAL_RDOC_OPTS.to_a.flatten + s.date = Time.now + if HAVE_EXT + s.extensions = EXT_CONF_FILES + s.require_paths << EXT_DIR + end + if HAVE_CERT + s.signing_key = CERT_PRIVATE + s.cert_chain = [CERT_PUBLIC] + end + s.platform = Gem::Platform::RUBY +# s.required_ruby_version = '>= 1.8.5' +} + diff --git a/mmap.c b/mmap.c deleted file mode 100644 index d77603f..0000000 --- a/mmap.c +++ /dev/null @@ -1,2303 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#if HAVE_SEMCTL && HAVE_SHMCTL -#include -#include -#include -#endif - -#include -#include -#include - -#ifndef StringValue -#define StringValue(x) do { \ - if (TYPE(x) != T_STRING) x = rb_str_to_str(x); \ -} while (0) -#endif - -#ifndef StringValuePtr -#define StringValuePtr(x) STR2CSTR(x) -#endif - -#ifndef SafeStringValue -#define SafeStringValue(x) Check_SafeStr(x) -#endif - -#ifndef MADV_NORMAL -#ifdef POSIX_MADV_NORMAL -#define MADV_NORMAL POSIX_MADV_NORMAL -#define MADV_RANDOM POSIX_MADV_RANDOM -#define MADV_SEQUENTIAL POSIX_MADV_SEQUENTIAL -#define MADV_WILLNEED POSIX_MADV_WILLNEED -#define MADV_DONTNEED POSIX_MADV_DONTNEED -#define madvise posix_madvise -#endif -#endif - -#define BEG(no) regs->beg[no] -#define END(no) regs->end[no] - -#ifndef MMAP_RETTYPE -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309 -#endif /* !_POSIX_C_SOURCE */ -#ifdef _POSIX_VERSION -#if _POSIX_VERSION >= 199309 -#define MMAP_RETTYPE void * -#endif /* _POSIX_VERSION >= 199309 */ -#endif /* _POSIX_VERSION */ -#endif /* !MMAP_RETTYPE */ - -#ifndef MMAP_RETTYPE -#define MMAP_RETTYPE caddr_t -#endif - -#ifndef MAP_FAILED -#define MAP_FAILED ((caddr_t)-1) -#endif /* !MAP_FAILED */ - -#ifndef MAP_ANON -#ifdef MAP_ANONYMOUS -#define MAP_ANON MAP_ANONYMOUS -#endif -#endif - -static VALUE mm_cMap; - -#define EXP_INCR_SIZE 4096 - -typedef struct { - MMAP_RETTYPE addr; - int smode, pmode, vscope; - int advice, flag; - VALUE key; - int semid, shmid; - size_t len, real, incr; - off_t offset; - char *path, *template; -} mm_mmap; - -typedef struct { - int count; - mm_mmap *t; -} mm_ipc; - -typedef struct { - VALUE obj, *argv; - int flag, id, argc; -} mm_bang; - -#define MM_MODIFY 1 -#define MM_ORIGIN 2 -#define MM_CHANGE (MM_MODIFY | 4) -#define MM_PROTECT 8 - -#define MM_FROZEN (1<<0) -#define MM_FIXED (1<<1) -#define MM_ANON (1<<2) -#define MM_LOCK (1<<3) -#define MM_IPC (1<<4) -#define MM_TMP (1<<5) - -#if HAVE_SEMCTL && HAVE_SHMCTL -static char template[1024]; - -union semun -{ - int val; - struct semid_ds *buf; - unsigned short int *array; - struct seminfo *__buf; -}; -#endif - -static void -mm_free(i_mm) - mm_ipc *i_mm; -{ -#if HAVE_SEMCTL && HAVE_SHMCTL - if (i_mm->t->flag & MM_IPC) { - struct shmid_ds buf; - - if (shmctl(i_mm->t->shmid, IPC_STAT, &buf) != -1) { - if (buf.shm_nattch == 1 && (i_mm->t->flag & MM_TMP)) { - semctl(i_mm->t->semid, 0, IPC_RMID); - if (i_mm->t->template) { - unlink(i_mm->t->template); - free(i_mm->t->template); - } - } - } - shmdt(i_mm->t); - } - else { - free(i_mm->t); - } -#endif - if (i_mm->t->path) { - munmap(i_mm->t->addr, i_mm->t->len); - if (i_mm->t->path != (char *)-1) { - if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE && - truncate(i_mm->t->path, i_mm->t->real) == -1) { - free(i_mm->t->path); - free(i_mm); - rb_raise(rb_eTypeError, "truncate"); - } - free(i_mm->t->path); - } - } - free(i_mm); -} - -static void -mm_lock(i_mm, wait_lock) - mm_ipc *i_mm; - int wait_lock; -{ -#if HAVE_SEMCTL && HAVE_SHMCTL - struct sembuf sem_op; - - if (i_mm->t->flag & MM_IPC) { - i_mm->count++; - if (i_mm->count == 1) { - retry: - sem_op.sem_num = 0; - sem_op.sem_op = -1; - sem_op.sem_flg = IPC_NOWAIT; - if (semop(i_mm->t->semid, &sem_op, 1) == -1) { - if (errno == EAGAIN) { - if (!wait_lock) { - rb_raise(rb_const_get(rb_mErrno, rb_intern("EAGAIN")), "EAGAIN"); - } - rb_thread_sleep(1); - goto retry; - } - rb_sys_fail("semop()"); - } - } - } -#endif -} - -static void -mm_unlock(i_mm) - mm_ipc *i_mm; -{ -#if HAVE_SEMCTL && HAVE_SHMCTL - struct sembuf sem_op; - - if (i_mm->t->flag & MM_IPC) { - i_mm->count--; - if (!i_mm->count) { - retry: - sem_op.sem_num = 0; - sem_op.sem_op = 1; - sem_op.sem_flg = IPC_NOWAIT; - if (semop(i_mm->t->semid, &sem_op, 1) == -1) { - if (errno == EAGAIN) { - rb_thread_sleep(1); - goto retry; - } - rb_sys_fail("semop()"); - } - } - } -#endif -} - -#define GetMmap(obj, i_mm, t_modify) \ - Data_Get_Struct(obj, mm_ipc, i_mm); \ - if (!i_mm->t->path) { \ - rb_raise(rb_eIOError, "unmapped file"); \ - } \ - if ((t_modify & MM_MODIFY) && (i_mm->t->flag & MM_FROZEN)) { \ - rb_error_frozen("mmap"); \ - } - -static VALUE -mm_vunlock(obj) - VALUE obj; -{ - mm_ipc *i_mm; - - GetMmap(obj, i_mm, 0); - mm_unlock(i_mm); - return Qnil; -} - -static VALUE -mm_semlock(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - mm_ipc *i_mm; - - GetMmap(obj, i_mm, 0); - if (!(i_mm->t->flag & MM_IPC)) { - rb_warning("useless use of #semlock"); - rb_yield(obj); - } - else { -#if HAVE_SEMCTL && HAVE_SHMCTL - VALUE a; - int wait_lock = Qtrue; - - if (rb_scan_args(argc, argv, "01", &a)) { - wait_lock = RTEST(a); - } - mm_lock(i_mm, wait_lock); - rb_ensure(rb_yield, obj, mm_vunlock, obj); -#endif - } - return Qnil; -} - -static VALUE -mm_ipc_key(obj) - VALUE obj; -{ - mm_ipc *i_mm; - - GetMmap(obj, i_mm, 0); - if (i_mm->t->flag & MM_IPC) { - return INT2NUM(i_mm->t->key); - } - return INT2NUM(-1); -} - -static VALUE -mm_unmap(obj) - VALUE obj; -{ - mm_ipc *i_mm; - - GetMmap(obj, i_mm, 0); - if (i_mm->t->path) { - mm_lock(i_mm, Qtrue); - munmap(i_mm->t->addr, i_mm->t->len); - if (i_mm->t->path != (char *)-1) { - if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE && - truncate(i_mm->t->path, i_mm->t->real) == -1) { - rb_raise(rb_eTypeError, "truncate"); - } - free(i_mm->t->path); - } - i_mm->t->path = '\0'; - mm_unlock(i_mm); - } - return Qnil; -} - -static VALUE -mm_freeze(obj) - VALUE obj; -{ - mm_ipc *i_mm; - rb_obj_freeze(obj); - GetMmap(obj, i_mm, 0); - i_mm->t->flag |= MM_FROZEN; - return obj; -} - -static VALUE -mm_str(obj, modify) - VALUE obj; - int modify; -{ - mm_ipc *i_mm; - VALUE ret = Qnil; - - GetMmap(obj, i_mm, modify & ~MM_ORIGIN); - if (modify & MM_MODIFY) { - if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap"); - if (!OBJ_TAINTED(ret) && rb_safe_level() >= 4) - rb_raise(rb_eSecurityError, "Insecure: can't modify mmap"); - } -#if HAVE_RB_DEFINE_ALLOC_FUNC - ret = rb_obj_alloc(rb_cString); - if (rb_obj_tainted(obj)) { - OBJ_TAINT(ret); - } -#else - if (rb_obj_tainted(obj)) { - ret = rb_tainted_str_new2(""); - } - else { - ret = rb_str_new2(""); - } - free(RSTRING(ret)->ptr); -#endif - RSTRING(ret)->ptr = i_mm->t->addr; - RSTRING(ret)->len = i_mm->t->real; - if (modify & MM_ORIGIN) { -#if HAVE_RB_DEFINE_ALLOC_FUNC - RSTRING(ret)->aux.shared = ret; - FL_SET(ret, ELTS_SHARED); -#else - RSTRING(ret)->orig = ret; -#endif - } - if (i_mm->t->flag & MM_FROZEN) { - ret = rb_obj_freeze(ret); - } - return ret; -} - -static VALUE -mm_to_str(obj) - VALUE obj; -{ - return mm_str(obj, MM_ORIGIN); -} - -extern char *ruby_strdup(); - -typedef struct { - mm_ipc *i_mm; - size_t len; -} mm_st; - -static VALUE -mm_i_expand(st_mm) - mm_st *st_mm; -{ - int fd; - mm_ipc *i_mm = st_mm->i_mm; - size_t len = st_mm->len; - - if (munmap(i_mm->t->addr, i_mm->t->len)) { - rb_raise(rb_eArgError, "munmap failed"); - } - if ((fd = open(i_mm->t->path, i_mm->t->smode)) == -1) { - rb_raise(rb_eArgError, "Can't open %s", i_mm->t->path); - } - if (len > i_mm->t->len) { - if (lseek(fd, len - i_mm->t->len - 1, SEEK_END) == -1) { - rb_raise(rb_eIOError, "Can't lseek %d", len - i_mm->t->len - 1); - } - if (write(fd, "\000", 1) != 1) { - rb_raise(rb_eIOError, "Can't extend %s", i_mm->t->path); - } - } - else if (len < i_mm->t->len && truncate(i_mm->t->path, len) == -1) { - rb_raise(rb_eIOError, "Can't truncate %s", i_mm->t->path); - } - i_mm->t->addr = mmap(0, len, i_mm->t->pmode, i_mm->t->vscope, fd, i_mm->t->offset); - close(fd); - if (i_mm->t->addr == MAP_FAILED) { - rb_raise(rb_eArgError, "mmap failed"); - } -#ifdef MADV_NORMAL - if (i_mm->t->advice && madvise(i_mm->t->addr, len, i_mm->t->advice) == -1) { - rb_raise(rb_eArgError, "madvise(%d)", errno); - } -#endif - if ((i_mm->t->flag & MM_LOCK) && mlock(i_mm->t->addr, len) == -1) { - rb_raise(rb_eArgError, "mlock(%d)", errno); - } - i_mm->t->len = len; - return Qnil; -} - -static void -mm_expandf(i_mm, len) - mm_ipc *i_mm; - size_t len; -{ - int status; - mm_st st_mm; - - if (i_mm->t->vscope == MAP_PRIVATE) { - rb_raise(rb_eTypeError, "expand for a private map"); - } - if (i_mm->t->flag & MM_FIXED) { - rb_raise(rb_eTypeError, "expand for a fixed map"); - } - if (!i_mm->t->path || i_mm->t->path == (char *)-1) { - rb_raise(rb_eTypeError, "expand for an anonymous map"); - } - st_mm.i_mm = i_mm; - st_mm.len = len; - if (i_mm->t->flag & MM_IPC) { - mm_lock(i_mm, Qtrue); - rb_protect(mm_i_expand, (VALUE)&st_mm, &status); - mm_unlock(i_mm); - if (status) { - rb_jump_tag(status); - } - } - else { - mm_i_expand(&st_mm); - } -} - -static void -mm_realloc(i_mm, len) - mm_ipc *i_mm; - size_t len; -{ - if (i_mm->t->flag & MM_FROZEN) rb_error_frozen("mmap"); - if (len > i_mm->t->len) { - if ((len - i_mm->t->len) < i_mm->t->incr) { - len = i_mm->t->len + i_mm->t->incr; - } - mm_expandf(i_mm, len); - } -} - -static VALUE -mm_extend(obj, a) - VALUE obj, a; -{ - mm_ipc *i_mm; - long len; - - GetMmap(obj, i_mm, MM_MODIFY); - len = NUM2LONG(a); - if (len > 0) { - mm_expandf(i_mm, i_mm->t->len + len); - } - return UINT2NUM(i_mm->t->len); -} - -static VALUE -mm_i_options(arg, obj) - VALUE arg, obj; -{ - mm_ipc *i_mm; - char *options; - VALUE key, value; - - Data_Get_Struct(obj, mm_ipc, i_mm); - key = rb_ary_entry(arg, 0); - value = rb_ary_entry(arg, 1); - key = rb_obj_as_string(key); - options = StringValuePtr(key); - if (strcmp(options, "length") == 0) { - i_mm->t->len = NUM2UINT(value); - if (i_mm->t->len <= 0) { - rb_raise(rb_eArgError, "Invalid value for length %d", i_mm->t->len); - } - i_mm->t->flag |= MM_FIXED; - } - else if (strcmp(options, "offset") == 0) { - i_mm->t->offset = NUM2INT(value); - if (i_mm->t->offset < 0) { - rb_raise(rb_eArgError, "Invalid value for offset %d", i_mm->t->offset); - } - i_mm->t->flag |= MM_FIXED; - } - else if (strcmp(options, "advice") == 0) { - i_mm->t->advice = NUM2INT(value); - } - else if (strcmp(options, "increment") == 0) { - int incr = NUM2INT(value); - if (incr < 0) { - rb_raise(rb_eArgError, "Invalid value for increment %d", incr); - } - i_mm->t->incr = incr; - } - else if (strcmp(options, "initialize") == 0) { - } -#if HAVE_SEMCTL && HAVE_SHMCTL - else if (strcmp(options, "ipc") == 0) { - if (value != Qtrue && TYPE(value) != T_HASH) { - rb_raise(rb_eArgError, "Expected an Hash for :ipc"); - } - i_mm->t->shmid = value; - i_mm->t->flag |= (MM_IPC | MM_TMP); - } -#endif - else { - rb_warning("Unknown option `%s'", options); - } - return Qnil; -} - -#if HAVE_SEMCTL && HAVE_SHMCTL - -static VALUE -mm_i_ipc(arg, obj) - VALUE arg, obj; -{ - mm_ipc *i_mm; - char *options; - VALUE key, value; - - Data_Get_Struct(obj, mm_ipc, i_mm); - key = rb_ary_entry(arg, 0); - value = rb_ary_entry(arg, 1); - key = rb_obj_as_string(key); - options = StringValuePtr(key); - if (strcmp(options, "key") == 0) { - i_mm->t->key = rb_funcall2(value, rb_intern("to_int"), 0, 0); - } - else if (strcmp(options, "permanent") == 0) { - if (RTEST(value)) { - i_mm->t->flag &= ~MM_TMP; - } - } - else if (strcmp(options, "mode") == 0) { - i_mm->t->semid = NUM2INT(value); - } - else { - rb_warning("Unknown option `%s'", options); - } - return Qnil; -} - -#endif - -static VALUE -mm_s_new(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - VALUE res = rb_funcall2(obj, rb_intern("allocate"), 0, 0); - rb_obj_call_init(res, argc, argv); - return res; -} - -static VALUE -mm_s_alloc(obj) - VALUE obj; -{ - VALUE res; - mm_ipc *i_mm; - - res = Data_Make_Struct(obj, mm_ipc, 0, mm_free, i_mm); - i_mm->t = ALLOC_N(mm_mmap, 1); - MEMZERO(i_mm->t, mm_mmap, 1); - i_mm->t->incr = EXP_INCR_SIZE; - return res; -} - -static VALUE -mm_init(argc, argv, obj) - VALUE obj, *argv; - int argc; -{ - struct stat st; - int fd, smode = 0, pmode = 0, vscope, perm, init; - MMAP_RETTYPE addr; - VALUE fname, fdv, vmode, scope, options; - mm_ipc *i_mm; - char *path, *mode; - size_t size = 0; - off_t offset; - int anonymous; - - options = Qnil; - if (argc > 1 && TYPE(argv[argc - 1]) == T_HASH) { - options = argv[argc - 1]; - argc--; - } - rb_scan_args(argc, argv, "12", &fname, &vmode, &scope); - vscope = 0; - path = 0; - fd = -1; - anonymous = 0; - fdv = Qnil; -#ifdef MAP_ANON - if (NIL_P(fname)) { - vscope = MAP_ANON | MAP_SHARED; - anonymous = 1; - } - else -#endif - { - if (rb_safe_level() > 0 && OBJ_TAINTED(fname)){ - rb_raise(rb_eSecurityError, "Insecure operation"); - } - rb_secure(4); - if (rb_respond_to(fname, rb_intern("fileno"))) { - fdv = rb_funcall2(fname, rb_intern("fileno"), 0, 0); - } - if (NIL_P(fdv)) { - fname = rb_str_to_str(fname); - SafeStringValue(fname); - path = StringValuePtr(fname); - } - else { - fd = NUM2INT(fdv); - if (fd < 0) { - rb_raise(rb_eArgError, "invalid file descriptor %d", fd); - } - } - if (!NIL_P(scope)) { - vscope = NUM2INT(scope); -#ifdef MAP_ANON - if (vscope & MAP_ANON) { - rb_raise(rb_eArgError, "filename specified for an anonymous map"); - } -#endif - } - } - vscope |= NIL_P(scope) ? MAP_SHARED : NUM2INT(scope); - size = 0; - perm = 0666; - if (!anonymous) { - if (NIL_P(vmode)) { - mode = "r"; - } - else if (rb_respond_to(vmode, rb_intern("to_ary"))) { - VALUE tmp; - - vmode = rb_convert_type(vmode, T_ARRAY, "Array", "to_ary"); - if (RARRAY(vmode)->len != 2) { - rb_raise(rb_eArgError, "Invalid length %d (expected 2)", - RARRAY(vmode)->len); - } - tmp = RARRAY(vmode)->ptr[0]; - mode = StringValuePtr(tmp); - perm = NUM2INT(RARRAY(vmode)->ptr[1]); - } - else { - mode = StringValuePtr(vmode); - } - if (strcmp(mode, "r") == 0) { - smode = O_RDONLY; - pmode = PROT_READ; - } - else if (strcmp(mode, "w") == 0) { - smode = O_RDWR | O_TRUNC; - pmode = PROT_READ | PROT_WRITE; - } - else if (strcmp(mode, "rw") == 0 || strcmp(mode, "wr") == 0) { - smode = O_RDWR; - pmode = PROT_READ | PROT_WRITE; - } - else if (strcmp(mode, "a") == 0) { - smode = O_RDWR | O_CREAT; - pmode = PROT_READ | PROT_WRITE; - } - else { - rb_raise(rb_eArgError, "Invalid mode %s", mode); - } - if (NIL_P(fdv)) { - if ((fd = open(path, smode, perm)) == -1) { - rb_raise(rb_eArgError, "Can't open %s", path); - } - } - if (fstat(fd, &st) == -1) { - rb_raise(rb_eArgError, "Can't stat %s", path); - } - size = st.st_size; - } - else { - fd = -1; - if (!NIL_P(vmode) && TYPE(vmode) != T_STRING) { - size = NUM2INT(vmode); - } - } - Data_Get_Struct(obj, mm_ipc, i_mm); - if (i_mm->t->flag & MM_FROZEN) { - rb_raise(rb_eArgError, "frozen mmap"); - } - i_mm->t->shmid = 0; - i_mm->t->semid = 0; - offset = 0; - if (options != Qnil) { - rb_iterate(rb_each, options, mm_i_options, obj); - if (path && (i_mm->t->len + i_mm->t->offset) > st.st_size) { - rb_raise(rb_eArgError, "invalid value for length (%d) or offset (%d)", - i_mm->t->len, i_mm->t->offset); - } - if (i_mm->t->len) size = i_mm->t->len; - offset = i_mm->t->offset; -#if HAVE_SEMCTL && HAVE_SHMCTL - if (i_mm->t->flag & MM_IPC) { - key_t key; - int shmid, semid, mode; - union semun sem_val; - struct shmid_ds buf; - mm_mmap *data; - - if (!(vscope & MAP_SHARED)) { - rb_warning("Probably it will not do what you expect ..."); - } - i_mm->t->key = -1; - i_mm->t->semid = 0; - if (TYPE(i_mm->t->shmid) == T_HASH) { - rb_iterate(rb_each, i_mm->t->shmid, mm_i_ipc, obj); - } - i_mm->t->shmid = 0; - if (i_mm->t->semid) { - mode = i_mm->t->semid; - i_mm->t->semid = 0; - } - else { - mode = 0644; - } - if ((int)i_mm->t->key <= 0) { - mode |= IPC_CREAT; - strcpy(template, "/tmp/ruby_mmap.XXXXXX"); - if (mkstemp(template) == -1) { - rb_sys_fail("mkstemp()"); - } - if ((key = ftok(template, 'R')) == -1) { - rb_sys_fail("ftok()"); - } - } - else { - key = (key_t)i_mm->t->key; - } - if ((shmid = shmget(key, sizeof(mm_ipc), mode)) == -1) { - rb_sys_fail("shmget()"); - } - data = shmat(shmid, (void *)0, 0); - if (data == (mm_mmap *)-1) { - rb_sys_fail("shmat()"); - } - if (i_mm->t->flag & MM_TMP) { - if (shmctl(shmid, IPC_RMID, &buf) == -1) { - rb_sys_fail("shmctl()"); - } - } - if ((semid = semget(key, 1, mode)) == -1) { - rb_sys_fail("semget()"); - } - if (mode & IPC_CREAT) { - sem_val.val = 1; - if (semctl(semid, 0, SETVAL, sem_val) == -1) { - rb_sys_fail("semctl()"); - } - } - memcpy(data, i_mm->t, sizeof(mm_mmap)); - free(i_mm->t); - i_mm->t = data; - i_mm->t->key = key; - i_mm->t->semid = semid; - i_mm->t->shmid = shmid; - if (i_mm->t->flag & MM_TMP) { - i_mm->t->template = ALLOC_N(char, strlen(template) + 1); - strcpy(i_mm->t->template, template); - } - } -#endif - } - init = 0; - if (anonymous) { - if (size <= 0) { - rb_raise(rb_eArgError, "length not specified for an anonymous map"); - } - if (offset) { - rb_warning("Ignoring offset for an anonymous map"); - offset = 0; - } - smode = O_RDWR; - pmode = PROT_READ | PROT_WRITE; - i_mm->t->flag |= MM_FIXED | MM_ANON; - } - else { - if (size == 0 && (smode & O_RDWR)) { - if (lseek(fd, i_mm->t->incr - 1, SEEK_END) == -1) { - rb_raise(rb_eIOError, "Can't lseek %d", i_mm->t->incr - 1); - } - if (write(fd, "\000", 1) != 1) { - rb_raise(rb_eIOError, "Can't extend %s", path); - } - init = 1; - size = i_mm->t->incr; - } - if (!NIL_P(fdv)) { - i_mm->t->flag |= MM_FIXED; - } - } - addr = mmap(0, size, pmode, vscope, fd, offset); - if (NIL_P(fdv) && !anonymous) { - close(fd); - } - if (addr == MAP_FAILED || !addr) { - rb_raise(rb_eArgError, "mmap failed (%d)", errno); - } -#ifdef MADV_NORMAL - if (i_mm->t->advice && madvise(addr, size, i_mm->t->advice) == -1) { - rb_raise(rb_eArgError, "madvise(%d)", errno); - } -#endif - if (anonymous && TYPE(options) == T_HASH) { - VALUE val; - char *ptr; - - val = rb_hash_aref(options, rb_str_new2("initialize")); - if (!NIL_P(val)) { - ptr = StringValuePtr(val); - memset(addr, ptr[0], size); - } - } - i_mm->t->addr = addr; - i_mm->t->len = size; - if (!init) i_mm->t->real = size; - i_mm->t->pmode = pmode; - i_mm->t->vscope = vscope; - i_mm->t->smode = smode & ~O_TRUNC; - i_mm->t->path = (path)?ruby_strdup(path):(char *)-1; - if (smode == O_RDONLY) { - obj = rb_obj_freeze(obj); - i_mm->t->flag |= MM_FROZEN; - } - else { - if (smode == O_WRONLY) { - i_mm->t->flag |= MM_FIXED; - } - OBJ_TAINT(obj); - } - return obj; -} - -static VALUE -mm_msync(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - mm_ipc *i_mm; - VALUE oflag; - int ret; - int flag = MS_SYNC; - - if (argc) { - rb_scan_args(argc, argv, "01", &oflag); - flag = NUM2INT(oflag); - } - GetMmap(obj, i_mm, MM_MODIFY); - if ((ret = msync(i_mm->t->addr, i_mm->t->len, flag)) != 0) { - rb_raise(rb_eArgError, "msync(%d)", ret); - } - if (i_mm->t->real < i_mm->t->len && i_mm->t->vscope != MAP_PRIVATE) - mm_expandf(i_mm, i_mm->t->real); - return obj; -} - -static VALUE -mm_mprotect(obj, a) - VALUE obj, a; -{ - mm_ipc *i_mm; - int ret, pmode; - char *smode; - - GetMmap(obj, i_mm, 0); - if (TYPE(a) == T_STRING) { - smode = StringValuePtr(a); - if (strcmp(smode, "r") == 0) pmode = PROT_READ; - else if (strcmp(smode, "w") == 0) pmode = PROT_WRITE; - else if (strcmp(smode, "rw") == 0 || strcmp(smode, "wr") == 0) - pmode = PROT_READ | PROT_WRITE; - else { - rb_raise(rb_eArgError, "Invalid mode %s", smode); - } - } - else { - pmode = NUM2INT(a); - } - if ((pmode & PROT_WRITE) && (i_mm->t->flag & MM_FROZEN)) - rb_error_frozen("mmap"); - if ((ret = mprotect(i_mm->t->addr, i_mm->t->len, pmode | PROT_READ)) != 0) { - rb_raise(rb_eArgError, "mprotect(%d)", ret); - } - i_mm->t->pmode = pmode; - if (pmode & PROT_READ) { - if (pmode & PROT_WRITE) i_mm->t->smode = O_RDWR; - else { - i_mm->t->smode = O_RDONLY; - obj = rb_obj_freeze(obj); - i_mm->t->flag |= MM_FROZEN; - } - } - else if (pmode & PROT_WRITE) { - i_mm->t->flag |= MM_FIXED; - i_mm->t->smode = O_WRONLY; - } - return obj; -} - -#ifdef MADV_NORMAL -static VALUE -mm_madvise(obj, a) - VALUE obj, a; -{ - mm_ipc *i_mm; - - GetMmap(obj, i_mm, 0); - if (madvise(i_mm->t->addr, i_mm->t->len, NUM2INT(a)) == -1) { - rb_raise(rb_eTypeError, "madvise(%d)", errno); - } - i_mm->t->advice = NUM2INT(a); - return Qnil; -} -#endif - -#define StringMmap(b, bp, bl) \ -do { \ - if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \ - mm_ipc *b_mm; \ - GetMmap(b, b_mm, 0); \ - bp = b_mm->t->addr; \ - bl = b_mm->t->real; \ - } \ - else { \ - bp = StringValuePtr(b); \ - bl = RSTRING(b)->len; \ - } \ -} while (0); - -static void -mm_update(str, beg, len, val) - mm_ipc *str; - VALUE val; - long beg; - long len; -{ - char *valp; - long vall; - - if (str->t->flag & MM_FROZEN) rb_error_frozen("mmap"); - if (len < 0) rb_raise(rb_eIndexError, "negative length %d", len); - mm_lock(str); - if (beg < 0) { - beg += str->t->real; - } - if (beg < 0 || str->t->real < (size_t)beg) { - if (beg < 0) { - beg -= str->t->real; - } - mm_unlock(str); - rb_raise(rb_eIndexError, "index %d out of string", beg); - } - if (str->t->real < (size_t)(beg + len)) { - len = str->t->real - beg; - } - - mm_unlock(str); - StringMmap(val, valp, vall); - mm_lock(str); - - if ((str->t->flag & MM_FIXED) && vall != len) { - mm_unlock(str); - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - if (len < vall) { - mm_realloc(str, str->t->real + vall - len); - } - - if (vall != len) { - memmove((char *)str->t->addr + beg + vall, - (char *)str->t->addr + beg + len, - str->t->real - (beg + len)); - } - if (str->t->real < (size_t)beg && len < 0) { - MEMZERO(str->t->addr + str->t->real, char, -len); - } - if (vall > 0) { - memmove((char *)str->t->addr + beg, valp, vall); - } - str->t->real += vall - len; - mm_unlock(str); -} - -static VALUE -mm_match(x, y) - VALUE x, y; -{ - VALUE reg, res; - long start; - - x = mm_str(x, MM_ORIGIN); - if (TYPE(y) == T_DATA && RDATA(y)->dfree == (RUBY_DATA_FUNC)mm_free) { - y = mm_to_str(y); - } - switch (TYPE(y)) { - case T_REGEXP: - res = rb_reg_match(y, x); - break; - - case T_STRING: - reg = rb_reg_regcomp(y); - start = rb_reg_search(reg, x, 0, 0); - if (start == -1) res = Qnil; - else res = INT2NUM(start); - break; - - default: - res = rb_funcall(y, rb_intern("=~"), 1, x); - break; - } - return res; -} - -static VALUE -get_pat(pat) - VALUE pat; -{ - switch (TYPE(pat)) { - case T_REGEXP: - break; - - case T_STRING: - pat = rb_reg_regcomp(pat); - break; - - default: - /* type failed */ - Check_Type(pat, T_REGEXP); - } - return pat; -} - -static int -mm_correct_backref() -{ - VALUE match; - int i, start; - - match = rb_backref_get(); - if (NIL_P(match)) return 0; - if (RMATCH(match)->BEG(0) == -1) return 0; - start = RMATCH(match)->BEG(0); - RMATCH(match)->str = rb_str_new(StringValuePtr(RMATCH(match)->str) + start, - RMATCH(match)->END(0) - start); - if (OBJ_TAINTED(match)) OBJ_TAINT(RMATCH(match)->str); - for (i = 0; i < RMATCH(match)->regs->num_regs && RMATCH(match)->BEG(i) != -1; i++) { - RMATCH(match)->BEG(i) -= start; - RMATCH(match)->END(i) -= start; - } - rb_backref_set(match); - return start; -} - -static VALUE -mm_sub_bang_int(bang_st) - mm_bang *bang_st; -{ - int argc = bang_st->argc; - VALUE *argv = bang_st->argv; - VALUE obj = bang_st->obj; - VALUE pat, repl = Qnil, match, str, res; - struct re_registers *regs; - int start, iter = 0; - int tainted = 0; - long plen; - mm_ipc *i_mm; - - if (argc == 1 && rb_block_given_p()) { - iter = 1; - } - else if (argc == 2) { - repl = rb_str_to_str(argv[1]); - if (OBJ_TAINTED(repl)) tainted = 1; - } - else { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); - } - GetMmap(obj, i_mm, MM_MODIFY); - str = mm_str(obj, MM_MODIFY | MM_ORIGIN); - - pat = get_pat(argv[0]); - res = Qnil; - if (rb_reg_search(pat, str, 0, 0) >= 0) { - start = mm_correct_backref(); - match = rb_backref_get(); - regs = RMATCH(match)->regs; - if (iter) { - rb_match_busy(match); - repl = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match))); - rb_backref_set(match); - } - else { - RSTRING(str)->ptr += start; - repl = rb_reg_regsub(repl, str, regs); - RSTRING(str)->ptr -= start; - } - if (OBJ_TAINTED(repl)) tainted = 1; - plen = END(0) - BEG(0); - if (RSTRING(repl)->len > plen) { - mm_realloc(i_mm, RSTRING(str)->len + RSTRING(repl)->len - plen); - RSTRING(str)->ptr = i_mm->t->addr; - } - if (RSTRING(repl)->len != plen) { - if (i_mm->t->flag & MM_FIXED) { - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - memmove(RSTRING(str)->ptr + start + BEG(0) + RSTRING(repl)->len, - RSTRING(str)->ptr + start + BEG(0) + plen, - RSTRING(str)->len - start - BEG(0) - plen); - } - memcpy(RSTRING(str)->ptr + start + BEG(0), - RSTRING(repl)->ptr, RSTRING(repl)->len); - i_mm->t->real += RSTRING(repl)->len - plen; - if (tainted) OBJ_TAINT(obj); - - res = obj; - } - rb_gc_force_recycle(str); - return res; -} - -static VALUE -mm_sub_bang(argc, argv, obj) - int argc; - VALUE *argv; - VALUE obj; -{ - VALUE res; - mm_bang bang_st; - mm_ipc *i_mm; - - bang_st.argc = argc; - bang_st.argv = argv; - bang_st.obj = obj; - GetMmap(obj, i_mm, MM_MODIFY); - if (i_mm->t->flag & MM_IPC) { - mm_lock(i_mm, Qtrue); - res = rb_ensure(mm_sub_bang_int, (VALUE)&bang_st, mm_vunlock, obj); - } - else { - res = mm_sub_bang_int(&bang_st); - } - return res; -} - -static VALUE -mm_gsub_bang_int(bang_st) - mm_bang *bang_st; -{ - int argc = bang_st->argc; - VALUE *argv = bang_st->argv; - VALUE obj = bang_st->obj; - VALUE pat, val, repl = Qnil, match, str; - struct re_registers *regs; - long beg, offset; - int start, iter = 0; - int tainted = 0; - long plen; - mm_ipc *i_mm; - - if (argc == 1 && rb_block_given_p()) { - iter = 1; - } - else if (argc == 2) { - repl = rb_str_to_str(argv[1]); - if (OBJ_TAINTED(repl)) tainted = 1; - } - else { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); - } - GetMmap(obj, i_mm, MM_MODIFY); - str = mm_str(obj, MM_MODIFY | MM_ORIGIN); - - pat = get_pat(argv[0]); - offset = 0; - beg = rb_reg_search(pat, str, 0, 0); - if (beg < 0) { - rb_gc_force_recycle(str); - return Qnil; - } - while (beg >= 0) { - start = mm_correct_backref(); - match = rb_backref_get(); - regs = RMATCH(match)->regs; - if (iter) { - rb_match_busy(match); - val = rb_obj_as_string(rb_yield(rb_reg_nth_match(0, match))); - rb_backref_set(match); - } - else { - RSTRING(str)->ptr += start; - val = rb_reg_regsub(repl, str, regs); - RSTRING(str)->ptr -= start; - } - if (OBJ_TAINTED(repl)) tainted = 1; - plen = END(0) - BEG(0); - if ((i_mm->t->real + RSTRING(val)->len - plen) > i_mm->t->len) { - mm_realloc(i_mm, RSTRING(str)->len + RSTRING(val)->len - plen); - } - if (RSTRING(val)->len != plen) { - if (i_mm->t->flag & MM_FIXED) { - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - memmove(RSTRING(str)->ptr + start + BEG(0) + RSTRING(val)->len, - RSTRING(str)->ptr + start + BEG(0) + plen, - RSTRING(str)->len - start - BEG(0) - plen); - } - memcpy(RSTRING(str)->ptr + start + BEG(0), - RSTRING(val)->ptr, RSTRING(val)->len); - RSTRING(str)->len += RSTRING(val)->len - plen; - i_mm->t->real = RSTRING(str)->len; - if (BEG(0) == END(0)) { - offset = start + END(0) + mbclen2(RSTRING(str)->ptr[END(0)], pat); - offset += RSTRING(val)->len - plen; - } - else { - offset = start + END(0) + RSTRING(val)->len - plen; - } - if (offset > RSTRING(str)->len) break; - beg = rb_reg_search(pat, str, offset, 0); - } - rb_backref_set(match); - if (tainted) OBJ_TAINT(obj); - rb_gc_force_recycle(str); - return obj; -} - -static VALUE -mm_gsub_bang(argc, argv, obj) - int argc; - VALUE *argv; - VALUE obj; -{ - VALUE res; - mm_bang bang_st; - mm_ipc *i_mm; - - bang_st.argc = argc; - bang_st.argv = argv; - bang_st.obj = obj; - GetMmap(obj, i_mm, MM_MODIFY); - if (i_mm->t->flag & MM_IPC) { - mm_lock(i_mm, Qtrue); - res = rb_ensure(mm_gsub_bang_int, (VALUE)&bang_st, mm_vunlock, obj); - } - else { - res = mm_gsub_bang_int(&bang_st); - } - return res; -} - -static VALUE mm_index __((int, VALUE *, VALUE)); - -#if HAVE_RB_DEFINE_ALLOC_FUNC - -static void -mm_subpat_set(obj, re, offset, val) - VALUE obj, re; - int offset; - VALUE val; -{ - VALUE str, match; - int start, end, len; - mm_ipc *i_mm; - - str = mm_str(obj, MM_MODIFY | MM_ORIGIN); - if (rb_reg_search(re, str, 0, 0) < 0) { - rb_raise(rb_eIndexError, "regexp not matched"); - } - match = rb_backref_get(); - if (offset >= RMATCH(match)->regs->num_regs) { - rb_raise(rb_eIndexError, "index %d out of regexp", offset); - } - - start = RMATCH(match)->BEG(offset); - if (start == -1) { - rb_raise(rb_eIndexError, "regexp group %d not matched", offset); - } - end = RMATCH(match)->END(offset); - len = end - start; - GetMmap(obj, i_mm, MM_MODIFY); - mm_update(i_mm, start, len, val); -} - -#endif - -static VALUE -mm_aset(str, indx, val) - VALUE str; - VALUE indx, val; -{ - long idx; - mm_ipc *i_mm; - - GetMmap(str, i_mm, MM_MODIFY); - switch (TYPE(indx)) { - case T_FIXNUM: - num_index: - idx = NUM2INT(indx); - if (idx < 0) { - idx += i_mm->t->real; - } - if (idx < 0 || i_mm->t->real <= (size_t)idx) { - rb_raise(rb_eIndexError, "index %d out of string", idx); - } - if (FIXNUM_P(val)) { - if (i_mm->t->real == (size_t)idx) { - i_mm->t->real += 1; - mm_realloc(i_mm, i_mm->t->real); - } - ((char *)i_mm->t->addr)[idx] = NUM2INT(val) & 0xff; - } - else { - mm_update(i_mm, idx, 1, val); - } - return val; - - case T_REGEXP: -#if HAVE_RB_DEFINE_ALLOC_FUNC - mm_subpat_set(str, indx, 0, val); -#else - { - VALUE args[2]; - args[0] = indx; - args[1] = val; - mm_sub_bang(2, args, str); - } -#endif - return val; - - case T_STRING: - { - VALUE res; - - res = mm_index(1, &indx, str); - if (!NIL_P(res)) { - mm_update(i_mm, NUM2LONG(res), RSTRING(indx)->len, val); - } - return val; - } - - default: - /* check if indx is Range */ - { - long beg, len; - if (rb_range_beg_len(indx, &beg, &len, i_mm->t->real, 2)) { - mm_update(i_mm, beg, len, val); - return val; - } - } - idx = NUM2LONG(indx); - goto num_index; - } -} - -static VALUE -mm_aset_m(argc, argv, str) - int argc; - VALUE *argv; - VALUE str; -{ - mm_ipc *i_mm; - - GetMmap(str, i_mm, MM_MODIFY); - if (argc == 3) { - long beg, len; - -#if HAVE_RB_DEFINE_ALLOC_FUNC - if (TYPE(argv[0]) == T_REGEXP) { - mm_subpat_set(str, argv[0], NUM2INT(argv[1]), argv[2]); - } - else -#endif - { - beg = NUM2INT(argv[0]); - len = NUM2INT(argv[1]); - mm_update(i_mm, beg, len, argv[2]); - } - return argv[2]; - } - if (argc != 2) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 2)", argc); - } - return mm_aset(str, argv[0], argv[1]); -} - -#if HAVE_RB_STR_INSERT - -static VALUE -mm_insert(str, idx, str2) - VALUE str, idx, str2; -{ - mm_ipc *i_mm; - long pos = NUM2LONG(idx); - - GetMmap(str, i_mm, MM_MODIFY); - if (pos == -1) { - pos = RSTRING(str)->len; - } - else if (pos < 0) { - pos++; - } - mm_update(i_mm, pos, 0, str2); - return str; -} - -#endif - -static VALUE mm_aref_m _((int, VALUE *, VALUE)); - -static VALUE -mm_slice_bang(argc, argv, str) - int argc; - VALUE *argv; - VALUE str; -{ - VALUE result; - VALUE buf[3]; - int i; - - if (argc < 1 || 2 < argc) { - rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)", argc); - } - for (i = 0; i < argc; i++) { - buf[i] = argv[i]; - } - buf[i] = rb_str_new(0,0); - result = mm_aref_m(argc, buf, str); - if (!NIL_P(result)) { - mm_aset_m(argc+1, buf, str); - } - return result; -} - -static VALUE -mm_cat(str, ptr, len) - VALUE str; - const char *ptr; - long len; -{ - mm_ipc *i_mm; - char *sptr; - - GetMmap(str, i_mm, MM_MODIFY); - if (len > 0) { - int poffset = -1; - sptr = (char *)i_mm->t->addr; - - if (sptr <= ptr && - ptr < sptr + i_mm->t->real) { - poffset = ptr - sptr; - } - mm_lock(i_mm, Qtrue); - mm_realloc(i_mm, i_mm->t->real + len); - sptr = (char *)i_mm->t->addr; - if (ptr) { - if (poffset >= 0) ptr = sptr + poffset; - memcpy(sptr + i_mm->t->real, ptr, len); - } - i_mm->t->real += len; - mm_unlock(i_mm); - } - return str; -} - -static VALUE -mm_append(str1, str2) - VALUE str1, str2; -{ - str2 = rb_str_to_str(str2); - str1 = mm_cat(str1, StringValuePtr(str2), RSTRING(str2)->len); - return str1; -} - -static VALUE -mm_concat(str1, str2) - VALUE str1, str2; -{ - if (FIXNUM_P(str2)) { - int i = FIX2INT(str2); - if (0 <= i && i <= 0xff) { /* byte */ - char c = i; - return mm_cat(str1, &c, 1); - } - } - str1 = mm_append(str1, str2); - return str1; -} - -#ifndef HAVE_RB_STR_LSTRIP - -static VALUE -mm_strip_bang(str) - VALUE str; -{ - char *s, *t, *e; - mm_ipc *i_mm; - - GetMmap(str, i_mm, MM_MODIFY); - mm_lock(i_mm, Qtrue); - s = (char *)i_mm->t->addr; - e = t = s + i_mm->t->real; - while (s < t && ISSPACE(*s)) s++; - t--; - while (s <= t && ISSPACE(*t)) t--; - t++; - - if (i_mm->t->real != (t - s) && (i_mm->t->flag & MM_FIXED)) { - mm_unlock(i_mm); - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - i_mm->t->real = t-s; - if (s > (char *)i_mm->t->addr) { - memmove(i_mm->t->addr, s, i_mm->t->real); - ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; - } - else if (t < e) { - ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; - } - else { - str = Qnil; - } - mm_unlock(i_mm); - return str; -} - -#else - -static VALUE -mm_lstrip_bang(str) - VALUE str; -{ - char *s, *t, *e; - mm_ipc *i_mm; - - GetMmap(str, i_mm, MM_MODIFY); - mm_lock(i_mm, Qtrue); - s = (char *)i_mm->t->addr; - e = t = s + i_mm->t->real; - while (s < t && ISSPACE(*s)) s++; - - if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) { - mm_unlock(i_mm); - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - i_mm->t->real = t - s; - if (s > (char *)i_mm->t->addr) { - memmove(i_mm->t->addr, s, i_mm->t->real); - ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; - mm_unlock(i_mm); - return str; - } - mm_unlock(i_mm); - return Qnil; -} - -static VALUE -mm_rstrip_bang(str) - VALUE str; -{ - char *s, *t, *e; - mm_ipc *i_mm; - - GetMmap(str, i_mm, MM_MODIFY); - mm_lock(i_mm, Qtrue); - s = (char *)i_mm->t->addr; - e = t = s + i_mm->t->real; - t--; - while (s <= t && ISSPACE(*t)) t--; - t++; - if (i_mm->t->real != (size_t)(t - s) && (i_mm->t->flag & MM_FIXED)) { - mm_unlock(i_mm); - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - i_mm->t->real = t - s; - if (t < e) { - ((char *)i_mm->t->addr)[i_mm->t->real] = '\0'; - mm_unlock(i_mm); - return str; - } - mm_unlock(i_mm); - return Qnil; -} - -static VALUE -mm_strip_bang(str) - VALUE str; -{ - VALUE l = mm_lstrip_bang(str); - VALUE r = mm_rstrip_bang(str); - - if (NIL_P(l) && NIL_P(r)) return Qnil; - return str; -} - -#endif - -#define MmapStr(b, recycle) \ -do { \ - recycle = 0; \ - if (TYPE(b) == T_DATA && RDATA(b)->dfree == (RUBY_DATA_FUNC)mm_free) { \ - recycle = 1; \ - b = mm_str(b, MM_ORIGIN); \ - } \ - else { \ - b = rb_str_to_str(b); \ - } \ -} while (0); - - -static VALUE -mm_cmp(a, b) - VALUE a, b; -{ - int result; - int recycle = 0; - - a = mm_str(a, MM_ORIGIN); - MmapStr(b, recycle); - result = rb_str_cmp(a, b); - rb_gc_force_recycle(a); - if (recycle) rb_gc_force_recycle(b); - return INT2FIX(result); -} - -#if HAVE_RB_STR_CASECMP - -static VALUE -mm_casecmp(a, b) - VALUE a, b; -{ - VALUE result; - int recycle = 0; - - a = mm_str(a, MM_ORIGIN); - MmapStr(b, recycle); - result = rb_funcall2(a, rb_intern("casecmp"), 1, &b); - rb_gc_force_recycle(a); - if (recycle) rb_gc_force_recycle(b); - return result; -} - -#endif - -static VALUE -mm_equal(a, b) - VALUE a, b; -{ - VALUE result; - mm_ipc *i_mm, *u_mm; - - if (a == b) return Qtrue; - if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free) - return Qfalse; - - GetMmap(a, i_mm, 0); - GetMmap(b, u_mm, 0); - if (i_mm->t->real != u_mm->t->real) - return Qfalse; - a = mm_str(a, MM_ORIGIN); - b = mm_str(b, MM_ORIGIN); - result = rb_funcall2(a, rb_intern("=="), 1, &b); - rb_gc_force_recycle(a); - rb_gc_force_recycle(b); - return result; -} - -static VALUE -mm_eql(a, b) - VALUE a, b; -{ - VALUE result; - mm_ipc *i_mm, *u_mm; - - if (a == b) return Qtrue; - if (TYPE(b) != T_DATA || RDATA(b)->dfree != (RUBY_DATA_FUNC)mm_free) - return Qfalse; - - GetMmap(a, i_mm, 0); - GetMmap(b, u_mm, 0); - if (i_mm->t->real != u_mm->t->real) - return Qfalse; - a = mm_str(a, MM_ORIGIN); - b = mm_str(b, MM_ORIGIN); - result = rb_funcall2(a, rb_intern("eql?"), 1, &b); - rb_gc_force_recycle(a); - rb_gc_force_recycle(b); - return result; -} - -static VALUE -mm_hash(a) - VALUE a; -{ - VALUE b; - int res; - - b = mm_str(a, MM_ORIGIN); - res = rb_str_hash(b); - rb_gc_force_recycle(b); - return INT2FIX(res); -} - -static VALUE -mm_size(a) - VALUE a; -{ - mm_ipc *i_mm; - - GetMmap(a, i_mm, 0); - return UINT2NUM(i_mm->t->real); -} - -static VALUE -mm_empty(a) - VALUE a; -{ - mm_ipc *i_mm; - - GetMmap(a, i_mm, 0); - if (i_mm->t->real == 0) return Qtrue; - return Qfalse; -} - -static VALUE -mm_protect_bang(t) - VALUE *t; -{ - return rb_funcall2(t[0], (ID)t[1], (int)t[2], (VALUE *)t[3]); -} - -static VALUE -mm_recycle(str) - VALUE str; -{ - rb_gc_force_recycle(str); - return str; -} - -static VALUE -mm_i_bang(bang_st) - mm_bang *bang_st; -{ - VALUE str, res; - mm_ipc *i_mm; - - str = mm_str(bang_st->obj, bang_st->flag); - if (bang_st->flag & MM_PROTECT) { - VALUE tmp[4]; - tmp[0] = str; - tmp[1] = (VALUE)bang_st->id; - tmp[2] = (VALUE)bang_st->argc; - tmp[3] = (VALUE)bang_st->argv; - res = rb_ensure(mm_protect_bang, (VALUE)tmp, mm_recycle, str); - } - else { - res = rb_funcall2(str, bang_st->id, bang_st->argc, bang_st->argv); - rb_gc_force_recycle(str); - } - if (res != Qnil) { - GetMmap(bang_st->obj, i_mm, 0); - i_mm->t->real = RSTRING(str)->len; - } - return res; -} - - -static VALUE -mm_bang_i(obj, flag, id, argc, argv) - VALUE obj, *argv; - int flag, id, argc; -{ - VALUE res; - mm_ipc *i_mm; - mm_bang bang_st; - - GetMmap(obj, i_mm, 0); - if ((flag & MM_CHANGE) && (i_mm->t->flag & MM_FIXED)) { - rb_raise(rb_eTypeError, "try to change the size of a fixed map"); - } - bang_st.obj = obj; - bang_st.flag = flag; - bang_st.id = id; - bang_st.argc = argc; - bang_st.argv = argv; - if (i_mm->t->flag & MM_IPC) { - mm_lock(i_mm, Qtrue); - res = rb_ensure(mm_i_bang, (VALUE)&bang_st, mm_vunlock, obj); - } - else { - res = mm_i_bang(&bang_st); - } - if (res == Qnil) return res; - return (flag & MM_ORIGIN)?res:obj; - -} - -#if HAVE_RB_STR_MATCH - -static VALUE -mm_match_m(a, b) - VALUE a, b; -{ - return mm_bang_i(a, MM_ORIGIN, rb_intern("match"), 1, &b); -} - -#endif - -static VALUE -mm_upcase_bang(a) - VALUE a; -{ - return mm_bang_i(a, MM_MODIFY, rb_intern("upcase!"), 0, 0); -} - -static VALUE -mm_downcase_bang(a) - VALUE a; -{ - return mm_bang_i(a, MM_MODIFY, rb_intern("downcase!"), 0, 0); -} - -static VALUE -mm_capitalize_bang(a) - VALUE a; -{ - return mm_bang_i(a, MM_MODIFY, rb_intern("capitalize!"), 0, 0); -} - -static VALUE -mm_swapcase_bang(a) - VALUE a; -{ - return mm_bang_i(a, MM_MODIFY, rb_intern("swapcase!"), 0, 0); -} - -static VALUE -mm_reverse_bang(a) - VALUE a; -{ - return mm_bang_i(a, MM_MODIFY, rb_intern("reverse!"), 0, 0); -} - -static VALUE -mm_chop_bang(a) - VALUE a; -{ - return mm_bang_i(a, MM_CHANGE, rb_intern("chop!"), 0, 0); -} - -static VALUE -mm_inspect(a) - VALUE a; -{ - return rb_any_to_s(a); -} - -static VALUE -mm_chomp_bang(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("chomp!"), argc, argv); -} - -static VALUE -mm_delete_bang(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("delete!"), argc, argv); -} - -static VALUE -mm_squeeze_bang(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("squeeze!"), argc, argv); -} - -static VALUE -mm_tr_bang(obj, a, b) - VALUE obj, a, b; -{ - VALUE tmp[2]; - tmp[0] = a; - tmp[1] = b; - return mm_bang_i(obj, MM_MODIFY | MM_PROTECT, rb_intern("tr!"), 2, tmp); -} - -static VALUE -mm_tr_s_bang(obj, a, b) - VALUE obj, a, b; -{ - VALUE tmp[2]; - tmp[0] = a; - tmp[1] = b; - return mm_bang_i(obj, MM_CHANGE | MM_PROTECT, rb_intern("tr_s!"), 2, tmp); -} - -static VALUE -mm_crypt(a, b) - VALUE a, b; -{ - return mm_bang_i(a, MM_ORIGIN, rb_intern("crypt"), 1, &b); -} - -static VALUE -mm_include(a, b) - VALUE a, b; -{ - return mm_bang_i(a, MM_ORIGIN, rb_intern("include?"), 1, &b); -} - -static VALUE -mm_index(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_ORIGIN, rb_intern("index"), argc, argv); -} - -static VALUE -mm_rindex(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_ORIGIN, rb_intern("rindex"), argc, argv); -} - -static VALUE -mm_aref_m(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_ORIGIN, rb_intern("[]"), argc, argv); -} - -static VALUE -mm_sum(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_ORIGIN, rb_intern("sum"), argc, argv); -} - -static VALUE -mm_split(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_ORIGIN, rb_intern("split"), argc, argv); -} - -static VALUE -mm_count(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - return mm_bang_i(obj, MM_ORIGIN, rb_intern("count"), argc, argv); -} - -static VALUE -mm_internal_each(tmp) - VALUE *tmp; -{ - return rb_funcall2(tmp[0], (ID)tmp[1], (int)tmp[2], (VALUE *)tmp[3]); -} - -static VALUE -mm_scan(obj, a) - VALUE obj, a; -{ - VALUE tmp[4]; - - if (!rb_block_given_p()) { - return rb_funcall(mm_str(obj, MM_ORIGIN), rb_intern("scan"), 1, a); - } - tmp[0] = mm_str(obj, MM_ORIGIN); - tmp[1] = (VALUE)rb_intern("scan"); - tmp[2] = (VALUE)1; - tmp[3] = (VALUE)&a; - rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); - return obj; -} - -static VALUE -mm_each_line(argc, argv, obj) - int argc; - VALUE obj, *argv; -{ - VALUE tmp[4]; - - tmp[0] = mm_str(obj, MM_ORIGIN); - tmp[1] = (VALUE)rb_intern("each_line"); - tmp[2] = (VALUE)argc; - tmp[3] = (VALUE)argv; - rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); - return obj; -} - -static VALUE -mm_each_byte(argc, argv, obj) - int argc; - VALUE obj, *argv; -{ - VALUE tmp[4]; - - tmp[0] = mm_str(obj, MM_ORIGIN); - tmp[1] = (VALUE)rb_intern("each_byte"); - tmp[2] = (VALUE)argc; - tmp[3] = (VALUE)argv; - rb_iterate(mm_internal_each, (VALUE)tmp, rb_yield, 0); - return obj; -} - -static VALUE -mm_undefined(argc, argv, obj) - int argc; - VALUE *argv, obj; -{ - rb_raise(rb_eNameError, "not yet implemented"); -} - -static VALUE -mm_mlockall(obj, flag) - VALUE obj, flag; -{ - if (mlockall(NUM2INT(flag)) == -1) { - rb_raise(rb_eArgError, "mlockall(%d)", errno); - } - return Qnil; -} - -static VALUE -mm_munlockall(obj) - VALUE obj; -{ - if (munlockall() == -1) { - rb_raise(rb_eArgError, "munlockall(%d)", errno); - } - return Qnil; -} - -static VALUE -mm_mlock(obj) - VALUE obj; -{ - mm_ipc *i_mm; - - Data_Get_Struct(obj, mm_ipc, i_mm); - if (i_mm->t->flag & MM_LOCK) { - return obj; - } - if (i_mm->t->flag & MM_ANON) { - rb_raise(rb_eArgError, "mlock(anonymous)"); - } - if (mlock(i_mm->t->addr, i_mm->t->len) == -1) { - rb_raise(rb_eArgError, "mlock(%d)", errno); - } - i_mm->t->flag |= MM_LOCK; - return obj; -} - -static VALUE -mm_munlock(obj) - VALUE obj; -{ - mm_ipc *i_mm; - - Data_Get_Struct(obj, mm_ipc, i_mm); - if (!(i_mm->t->flag & MM_LOCK)) { - return obj; - } - if (munlock(i_mm->t->addr, i_mm->t->len) == -1) { - rb_raise(rb_eArgError, "munlock(%d)", errno); - } - i_mm->t->flag &= ~MM_LOCK; - return obj; -} - -void -Init_mmap() -{ - if (rb_const_defined_at(rb_cObject, rb_intern("Mmap"))) { - rb_raise(rb_eNameError, "class already defined"); - } - mm_cMap = rb_define_class("Mmap", rb_cObject); - rb_define_const(mm_cMap, "MS_SYNC", INT2FIX(MS_SYNC)); - rb_define_const(mm_cMap, "MS_ASYNC", INT2FIX(MS_ASYNC)); - rb_define_const(mm_cMap, "MS_INVALIDATE", INT2FIX(MS_INVALIDATE)); - rb_define_const(mm_cMap, "PROT_READ", INT2FIX(PROT_READ)); - rb_define_const(mm_cMap, "PROT_WRITE", INT2FIX(PROT_WRITE)); - rb_define_const(mm_cMap, "PROT_EXEC", INT2FIX(PROT_EXEC)); - rb_define_const(mm_cMap, "PROT_NONE", INT2FIX(PROT_NONE)); - rb_define_const(mm_cMap, "MAP_SHARED", INT2FIX(MAP_SHARED)); - rb_define_const(mm_cMap, "MAP_PRIVATE", INT2FIX(MAP_PRIVATE)); -#ifdef MADV_NORMAL - rb_define_const(mm_cMap, "MADV_NORMAL", INT2FIX(MADV_NORMAL)); - rb_define_const(mm_cMap, "MADV_RANDOM", INT2FIX(MADV_RANDOM)); - rb_define_const(mm_cMap, "MADV_SEQUENTIAL", INT2FIX(MADV_SEQUENTIAL)); - rb_define_const(mm_cMap, "MADV_WILLNEED", INT2FIX(MADV_WILLNEED)); - rb_define_const(mm_cMap, "MADV_DONTNEED", INT2FIX(MADV_DONTNEED)); -#endif -#ifdef MAP_DENYWRITE - rb_define_const(mm_cMap, "MAP_DENYWRITE", INT2FIX(MAP_DENYWRITE)); -#endif -#ifdef MAP_EXECUTABLE - rb_define_const(mm_cMap, "MAP_EXECUTABLE", INT2FIX(MAP_EXECUTABLE)); -#endif -#ifdef MAP_NORESERVE - rb_define_const(mm_cMap, "MAP_NORESERVE", INT2FIX(MAP_NORESERVE)); -#endif -#ifdef MAP_LOCKED - rb_define_const(mm_cMap, "MAP_LOCKED", INT2FIX(MAP_LOCKED)); -#endif -#ifdef MAP_GROWSDOWN - rb_define_const(mm_cMap, "MAP_GROWSDOWN", INT2FIX(MAP_GROWSDOWN)); -#endif -#ifdef MAP_ANON - rb_define_const(mm_cMap, "MAP_ANON", INT2FIX(MAP_ANON)); -#endif -#ifdef MAP_ANONYMOUS - rb_define_const(mm_cMap, "MAP_ANONYMOUS", INT2FIX(MAP_ANONYMOUS)); -#endif -#ifdef MAP_NOSYNC - rb_define_const(mm_cMap, "MAP_NOSYNC", INT2FIX(MAP_NOSYNC)); -#endif -#ifdef MCL_CURRENT - rb_define_const(mm_cMap, "MCL_CURRENT", INT2FIX(MCL_CURRENT)); - rb_define_const(mm_cMap, "MCL_FUTURE", INT2FIX(MCL_FUTURE)); -#endif - rb_include_module(mm_cMap, rb_mComparable); - rb_include_module(mm_cMap, rb_mEnumerable); - -#if HAVE_RB_DEFINE_ALLOC_FUNC - rb_define_alloc_func(mm_cMap, mm_s_alloc); -#else - rb_define_singleton_method(mm_cMap, "allocate", mm_s_alloc, 0); -#endif - rb_define_singleton_method(mm_cMap, "new", mm_s_new, -1); - rb_define_singleton_method(mm_cMap, "mlockall", mm_mlockall, 1); - rb_define_singleton_method(mm_cMap, "lockall", mm_mlockall, 1); - rb_define_singleton_method(mm_cMap, "munlockall", mm_munlockall, 0); - rb_define_singleton_method(mm_cMap, "unlockall", mm_munlockall, 0); - - rb_define_method(mm_cMap, "initialize", mm_init, -1); - - rb_define_method(mm_cMap, "unmap", mm_unmap, 0); - rb_define_method(mm_cMap, "munmap", mm_unmap, 0); - rb_define_method(mm_cMap, "msync", mm_msync, -1); - rb_define_method(mm_cMap, "sync", mm_msync, -1); - rb_define_method(mm_cMap, "flush", mm_msync, -1); - rb_define_method(mm_cMap, "mprotect", mm_mprotect, 1); - rb_define_method(mm_cMap, "protect", mm_mprotect, 1); -#ifdef MADV_NORMAL - rb_define_method(mm_cMap, "madvise", mm_madvise, 1); - rb_define_method(mm_cMap, "advise", mm_madvise, 1); -#endif - rb_define_method(mm_cMap, "mlock", mm_mlock, 0); - rb_define_method(mm_cMap, "lock", mm_mlock, 0); - rb_define_method(mm_cMap, "munlock", mm_munlock, 0); - rb_define_method(mm_cMap, "unlock", mm_munlock, 0); - - rb_define_method(mm_cMap, "extend", mm_extend, 1); - rb_define_method(mm_cMap, "freeze", mm_freeze, 0); - rb_define_method(mm_cMap, "clone", mm_undefined, -1); - rb_define_method(mm_cMap, "initialize_copy", mm_undefined, -1); - rb_define_method(mm_cMap, "dup", mm_undefined, -1); - rb_define_method(mm_cMap, "<=>", mm_cmp, 1); - rb_define_method(mm_cMap, "==", mm_equal, 1); - rb_define_method(mm_cMap, "===", mm_equal, 1); - rb_define_method(mm_cMap, "eql?", mm_eql, 1); - rb_define_method(mm_cMap, "hash", mm_hash, 0); -#if HAVE_RB_STR_CASECMP - rb_define_method(mm_cMap, "casecmp", mm_casecmp, 1); -#endif - rb_define_method(mm_cMap, "+", mm_undefined, -1); - rb_define_method(mm_cMap, "*", mm_undefined, -1); - rb_define_method(mm_cMap, "%", mm_undefined, -1); - rb_define_method(mm_cMap, "[]", mm_aref_m, -1); - rb_define_method(mm_cMap, "[]=", mm_aset_m, -1); -#if HAVE_RB_STR_INSERT - rb_define_method(mm_cMap, "insert", mm_insert, 2); -#endif - rb_define_method(mm_cMap, "length", mm_size, 0); - rb_define_method(mm_cMap, "size", mm_size, 0); - rb_define_method(mm_cMap, "empty?", mm_empty, 0); - rb_define_method(mm_cMap, "=~", mm_match, 1); - rb_define_method(mm_cMap, "~", mm_undefined, -1); -#if HAVE_RB_STR_MATCH - rb_define_method(mm_cMap, "match", mm_match_m, 1); -#endif - rb_define_method(mm_cMap, "succ", mm_undefined, -1); - rb_define_method(mm_cMap, "succ!", mm_undefined, -1); - rb_define_method(mm_cMap, "next", mm_undefined, -1); - rb_define_method(mm_cMap, "next!", mm_undefined, -1); - rb_define_method(mm_cMap, "upto", mm_undefined, -1); - rb_define_method(mm_cMap, "index", mm_index, -1); - rb_define_method(mm_cMap, "rindex", mm_rindex, -1); - rb_define_method(mm_cMap, "replace", mm_undefined, -1); - - rb_define_method(mm_cMap, "to_i", mm_undefined, -1); - rb_define_method(mm_cMap, "to_f", mm_undefined, -1); - rb_define_method(mm_cMap, "to_sym", mm_undefined, -1); - rb_define_method(mm_cMap, "to_s", rb_any_to_s, 0); - rb_define_method(mm_cMap, "to_str", mm_to_str, 0); - rb_define_method(mm_cMap, "inspect", mm_inspect, 0); - rb_define_method(mm_cMap, "dump", mm_undefined, -1); - - rb_define_method(mm_cMap, "upcase", mm_undefined, -1); - rb_define_method(mm_cMap, "downcase", mm_undefined, -1); - rb_define_method(mm_cMap, "capitalize", mm_undefined, -1); - rb_define_method(mm_cMap, "swapcase", mm_undefined, -1); - - rb_define_method(mm_cMap, "upcase!", mm_upcase_bang, 0); - rb_define_method(mm_cMap, "downcase!", mm_downcase_bang, 0); - rb_define_method(mm_cMap, "capitalize!", mm_capitalize_bang, 0); - rb_define_method(mm_cMap, "swapcase!", mm_swapcase_bang, 0); - - rb_define_method(mm_cMap, "hex", mm_undefined, -1); - rb_define_method(mm_cMap, "oct", mm_undefined, -1); - rb_define_method(mm_cMap, "split", mm_split, -1); - rb_define_method(mm_cMap, "reverse", mm_undefined, -1); - rb_define_method(mm_cMap, "reverse!", mm_reverse_bang, 0); - rb_define_method(mm_cMap, "concat", mm_concat, 1); - rb_define_method(mm_cMap, "<<", mm_concat, 1); - rb_define_method(mm_cMap, "crypt", mm_crypt, 1); - rb_define_method(mm_cMap, "intern", mm_undefined, -1); - - rb_define_method(mm_cMap, "include?", mm_include, 1); - - rb_define_method(mm_cMap, "scan", mm_scan, 1); - - rb_define_method(mm_cMap, "ljust", mm_undefined, -1); - rb_define_method(mm_cMap, "rjust", mm_undefined, -1); - rb_define_method(mm_cMap, "center", mm_undefined, -1); - - rb_define_method(mm_cMap, "sub", mm_undefined, -1); - rb_define_method(mm_cMap, "gsub", mm_undefined, -1); - rb_define_method(mm_cMap, "chop", mm_undefined, -1); - rb_define_method(mm_cMap, "chomp", mm_undefined, -1); - rb_define_method(mm_cMap, "strip", mm_undefined, -1); -#if HAVE_RB_STR_LSTRIP - rb_define_method(mm_cMap, "lstrip", mm_undefined, -1); - rb_define_method(mm_cMap, "rstrip", mm_undefined, -1); -#endif - - rb_define_method(mm_cMap, "sub!", mm_sub_bang, -1); - rb_define_method(mm_cMap, "gsub!", mm_gsub_bang, -1); - rb_define_method(mm_cMap, "strip!", mm_strip_bang, 0); -#if HAVE_RB_STR_LSTRIP - rb_define_method(mm_cMap, "lstrip!", mm_lstrip_bang, 0); - rb_define_method(mm_cMap, "rstrip!", mm_rstrip_bang, 0); -#endif - rb_define_method(mm_cMap, "chop!", mm_chop_bang, 0); - rb_define_method(mm_cMap, "chomp!", mm_chomp_bang, -1); - - rb_define_method(mm_cMap, "tr", mm_undefined, -1); - rb_define_method(mm_cMap, "tr_s", mm_undefined, -1); - rb_define_method(mm_cMap, "delete", mm_undefined, -1); - rb_define_method(mm_cMap, "squeeze", mm_undefined, -1); - rb_define_method(mm_cMap, "count", mm_count, -1); - - rb_define_method(mm_cMap, "tr!", mm_tr_bang, 2); - rb_define_method(mm_cMap, "tr_s!", mm_tr_s_bang, 2); - rb_define_method(mm_cMap, "delete!", mm_delete_bang, -1); - rb_define_method(mm_cMap, "squeeze!", mm_squeeze_bang, -1); - - rb_define_method(mm_cMap, "each_line", mm_each_line, -1); - rb_define_method(mm_cMap, "each", mm_each_line, -1); - rb_define_method(mm_cMap, "each_byte", mm_each_byte, -1); - - rb_define_method(mm_cMap, "sum", mm_sum, -1); - - rb_define_method(mm_cMap, "slice", mm_aref_m, -1); - rb_define_method(mm_cMap, "slice!", mm_slice_bang, -1); - rb_define_method(mm_cMap, "semlock", mm_semlock, -1); - rb_define_method(mm_cMap, "ipc_key", mm_ipc_key, 0); -} diff --git a/mmap.html b/mmap.html deleted file mode 100644 index 2d78ddc..0000000 --- a/mmap.html +++ /dev/null @@ -1,272 +0,0 @@ - - - - -Untitled - - -

Mmap

-

Download

-

The Mmap class implement memory-mapped file objects

-

WARNING

-

The variables $' and $` are not available with gsub! and sub!

-

SuperClass

-

Object

-

Included Modules

-
    -
  • Comparable
  • -
  • Enumerable
  • -
-

Class Methods

-
-
lockall(flag)
-
-disable paging of all pages mapped. flag can be -Mmap::MCL_CURRENT or Mmap::MCL_FUTURE
-
new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {})
-
new(nil, length, protection = Mmap::MAP_SHARED, options = {})
-
-create a new Mmap object -
-
file
-
-Pathname of the file, if nil is given an anonymous map -is created Mmanp::MAP_ANON -
-
mode
-
-Mode to open the file, it can be "r", "w", "rw", "a" -
-
protection
-
-specify the nature of the mapping -
-
Mmap::MAP_SHARED
-
-Creates a mapping that's shared with all other processes -mapping the same areas of the file. -The default value is Mmap::MAP_SHARED -
-
Mmap::MAP_PRIVATE
-
-Creates a private copy-on-write mapping, so changes to the -contents of the mmap object will be private to this process -
-
-
-
options
-
-Hash. If one of the options length or offset -is specified it will not possible to modify the size of -the mapped file. -
-
length
-
-Maps length bytes from the file -
-
offset
-
-The mapping begin at offset -
-
advice
-
-The type of the access (see #madvise) -
-
-
-
-
unlockall
-
-reenable paging
-
-

Methods

-
-
extend(count)
-
-add count bytes to the file (i.e. pre-extend the file)
-
madvise(advice)
-
-advice can have the value Mmap::MADV_NORMAL, -Mmap::MADV_RANDOM, Mmap::MADV_SEQUENTIAL, -Mmap::MADV_WILLNEED, Mmap::MADV_DONTNEED
-
mprotect(mode)
-
-change the mode, value must be "r", "w" or "rw"
-
mlock
-
-disable paging
-
msync
-
flush
-
-flush the file
-
munlock
-
-reenable paging
-
munmap
-
-terminate the association
-
-

Other methods with the same syntax than for the class String

-
-
self == other
-
-comparison
-
self > other
-
-comparison
-
self >= other
-
-comparison
-
self < other
-
-comparison
-
self <= other
-
-comparison
-
self === other
-
-used for case comparison
-
self << other
-
-append other to self
-
self =~ other
-
-return an index of the match
-
self[nth]
-
-retrieve the nth character
-
self[start..last]
-
-return a substring from start to last
-
self[start, length]
-
-return a substring of lenght characters from start
-
self[nth] = val
-
-change the nth character with val
-
self[start..last] = val
-
-change substring from start to last with val
-
self[start, len] = val
-
-replace length characters from start with val.
-
self <=> other
-
-comparison : return -1, 0, 1
-
casecmp(other) >= 1.7.1
-
concat(other)
-
-append the contents of other
-
capitalize!
-
-change the first character to uppercase letter
-
chop!
-
-chop off the last character
-
chomp!([rs])
-
-chop off the line ending character, specified by rs
-
count(o1 [, o2, ...])
-
-each parameter defines a set of character to count
-
crypt(salt)
-
-crypt with salt
-
delete!(str)
-
-delete every characters included in str
-
downcase!
-
-change all uppercase character to lowercase character
-
each_byte {|char|...}
-
-iterate on each byte
-
each([rs]) {|line|...}
-
each_line([rs]) {|line|...}
-
-iterate on each line
-
empty?
-
-return true if the file is empty
-
freeze
-
-freeze the current file
-
frozen
-
-return true if the file is frozen
-
gsub!(pattern, replace)
-
-global substitution
-
gsub!(pattern) {|str|...}
-
-global substitution
-
include?(other)
-
-return true if other is found
-
index(substr[, pos])
-
-return the index of substr
-
insert(index, str) >= 1.7.1
-
-insert str at index
-
length
-
-return the size of the file
-
reverse!
-
-reverse the content of the file
-
rindex(substr[, pos])
-
-return the index of the last occurrence of substr
-
scan(pattern)
-
-return an array of all occurence matched by pattern
-
scan(pattern) {|str| ...}
-
-iterate through the file, matching the pattern
-
size
-
-return the size of the file
-
slice
-
-same than []
-
slice!
-
-delete the specified portion of the file
-
split([sep[, limit]])
-
-splits into a list of strings and return this array
-
squeeze!([str])
-
-squeezes sequences of the same characters which is included in str
-
strip!
-
-removes leading and trailing whitespace
-
sub!(pattern, replace)
-
-substitution
-
sub!(pattern) {|str| ...}
-
-substitution
-
sum([bits])
-
-return a checksum
-
swapcase!
-
-replaces all lowercase characters to uppercase characters, and vice-versa
-
tr!(search, replace)
-
-translate the character from search to replace
-
tr_s!(search, replace)
-
-translate the character from search to replace, then -squeeze sequence of the same characters
-
upcase!
-
-replaces all lowercase characters to downcase characters
-
- - - diff --git a/mmap.rd b/mmap.rd deleted file mode 100644 index 622ad89..0000000 --- a/mmap.rd +++ /dev/null @@ -1,253 +0,0 @@ -=begin -= Mmap - -(()) - -The Mmap class implement memory-mapped file objects - -=== WARNING -=== The variables $' and $` are not available with gsub! and sub! - -== SuperClass - -Object - -== Included Modules - -* Comparable -* Enumerable - -== Class Methods - ---- lockall(flag) - disable paging of all pages mapped. ((|flag|)) can be - ((|Mmap::MCL_CURRENT|)) or ((|Mmap::MCL_FUTURE|)) - ---- new(file, mode = "r", protection = Mmap::MAP_SHARED, options = {}) ---- new(nil, length, protection = Mmap::MAP_SHARED, options = {}) - create a new Mmap object - - : ((|file|)) - Pathname of the file, if ((|nil|)) is given an anonymous map - is created ((|Mmanp::MAP_ANON|)) - - : ((|mode|)) - Mode to open the file, it can be "r", "w", "rw", "a" - - : ((|protection|)) - specify the nature of the mapping - - : ((|Mmap::MAP_SHARED|)) - Creates a mapping that's shared with all other processes - mapping the same areas of the file. - The default value is ((|Mmap::MAP_SHARED|)) - - : ((|Mmap::MAP_PRIVATE|)) - Creates a private copy-on-write mapping, so changes to the - contents of the mmap object will be private to this process - - : ((|options|)) - Hash. If one of the options ((|length|)) or ((|offset|)) - is specified it will not possible to modify the size of - the mapped file. - - : ((|length|)) - Maps ((|length|)) bytes from the file - - : ((|offset|)) - The mapping begin at ((|offset|)) - - : ((|advice|)) - The type of the access (see #madvise) - - ---- unlockall - reenable paging - -== Methods - ---- extend(count) - add ((|count|)) bytes to the file (i.e. pre-extend the file) - ---- madvise(advice) - ((|advice|)) can have the value ((|Mmap::MADV_NORMAL|)), - ((|Mmap::MADV_RANDOM|)), ((|Mmap::MADV_SEQUENTIAL|)), - ((|Mmap::MADV_WILLNEED|)), ((|Mmap::MADV_DONTNEED|)) - ---- mprotect(mode) - change the mode, value must be "r", "w" or "rw" - ---- mlock - disable paging - ---- msync ---- flush - flush the file - ---- munlock - reenable paging - ---- munmap - terminate the association - -=== Other methods with the same syntax than for the class String - - ---- self == other - comparison - ---- self > other - comparison - ---- self >= other - comparison - ---- self < other - comparison - ---- self <= other - comparison - ---- self === other - used for ((|case|)) comparison - ---- self << other - append ((|other|)) to ((|self|)) - ---- self =~ other - return an index of the match - ---- self[nth] - retrieve the ((|nth|)) character - ---- self[start..last] - return a substring from ((|start|)) to ((|last|)) - ---- self[start, length] - return a substring of ((|lenght|)) characters from ((|start|)) - ---- self[nth] = val - change the ((|nth|)) character with ((|val|)) - ---- self[start..last] = val - change substring from ((|start|)) to ((|last|)) with ((|val|)) - ---- self[start, len] = val - replace ((|length|)) characters from ((|start|)) with ((|val|)). - ---- self <=> other - comparison : return -1, 0, 1 - ---- casecmp(other) >= 1.7.1 - ---- concat(other) - append the contents of ((|other|)) - ---- capitalize! - change the first character to uppercase letter - ---- chop! - chop off the last character - ---- chomp!([rs]) - chop off the line ending character, specified by ((|rs|)) - ---- count(o1 [, o2, ...]) - each parameter defines a set of character to count - ---- crypt(salt) - crypt with ((|salt|)) - ---- delete!(str) - delete every characters included in ((|str|)) - ---- downcase! - change all uppercase character to lowercase character - ---- each_byte {|char|...} - iterate on each byte - ---- each([rs]) {|line|...} ---- each_line([rs]) {|line|...} - iterate on each line - ---- empty? - return ((|true|)) if the file is empty - ---- freeze - freeze the current file - ---- frozen - return ((|true|)) if the file is frozen - ---- gsub!(pattern, replace) - global substitution - ---- gsub!(pattern) {|str|...} - global substitution - ---- include?(other) - return ((|true|)) if ((|other|)) is found - ---- index(substr[, pos]) - return the index of ((|substr|)) - ---- insert(index, str) >= 1.7.1 - insert ((|str|)) at ((|index|)) - ---- length - return the size of the file - ---- reverse! - reverse the content of the file - ---- rindex(substr[, pos]) - return the index of the last occurrence of ((|substr|)) - ---- scan(pattern) - return an array of all occurence matched by ((|pattern|)) - ---- scan(pattern) {|str| ...} - iterate through the file, matching the ((|pattern|)) - ---- size - return the size of the file - ---- slice - same than ((|[]|)) - ---- slice! - delete the specified portion of the file - ---- split([sep[, limit]]) - splits into a list of strings and return this array - ---- squeeze!([str]) - squeezes sequences of the same characters which is included in ((|str|)) - ---- strip! - removes leading and trailing whitespace - ---- sub!(pattern, replace) - substitution - ---- sub!(pattern) {|str| ...} - substitution - ---- sum([bits]) - return a checksum - ---- swapcase! - replaces all lowercase characters to uppercase characters, and vice-versa - ---- tr!(search, replace) - translate the character from ((|search|)) to ((|replace|)) - ---- tr_s!(search, replace) - translate the character from ((|search|)) to ((|replace|)), then - squeeze sequence of the same characters - ---- upcase! - replaces all lowercase characters to downcase characters - -=end diff --git a/prj_cfg.rb b/prj_cfg.rb new file mode 100644 index 0000000..dd89cb6 --- /dev/null +++ b/prj_cfg.rb @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# PROJECT +PROJECT_NAME = 'mmap' +PROJECT_SUMMARY = 'The Mmap class implement memory-mapped file objects' +PROJECT_HOMEPAGE= 'http://asynk.ch' +UNIX_NAME = File.basename( Dir.pwd ).downcase + +# PERSONAL +USER_NAME = 'Jérémy Zurcher' || ENV['USERNAME'] +USER_EMAIL = 'jeremy@asynk.ch' || ENV['EMAIL'] +RUBYFORGE_USER = ENV['RUBYFORGE_USER'] + +# VERSION +PROJECT_VERSION = '0.1' + +# DIRECTORIES +BIN_DIR = 'bin' +LIB_DIR = 'lib' +EXT_DIR = 'ext' +TEST_DIR = 'test' +RDOC_DIR = 'rdoc' +RCOV_DIR = 'rcov' +RAKE_DIR = 'tasks' + +# FILES +README = 'README' +CHANGES = 'CHANGES' +LICENSE = 'COPYING' +RAKEFILE = 'Rakefile' +SETUP_FILE = 'setup.rb' +PRJ_FILE = 'prj_cfg.rb' +GEM_SPEC_FILE = 'gem_spec.rb' +DATA_FILES = FileList["benchmarks/**/*", "examples/**/*", "manual/**/*", "test/TestSuite/**/*" ] + diff --git a/setup.rb b/setup.rb new file mode 100644 index 0000000..c0b6da7 --- /dev/null +++ b/setup.rb @@ -0,0 +1,1596 @@ +# +# setup.rb +# +# Copyright (c) 2000-2006 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the terms of +# the GNU LGPL, Lesser General Public License version 2.1. +# + +unless Enumerable.method_defined?(:map) # Ruby 1.4.6 + module Enumerable + alias map collect + end +end + +unless File.respond_to?(:read) # Ruby 1.6 + def File.read(fname) + open(fname) {|f| + return f.read + } + end +end + +unless Errno.const_defined?(:ENOTEMPTY) # Windows? + module Errno + class ENOTEMPTY + # We do not raise this exception, implementation is not needed. + end + end +end + +def File.binread(fname) + open(fname, 'rb') {|f| + return f.read + } +end + +# for corrupted Windows' stat(2) +def File.dir?(path) + File.directory?((path[-1,1] == '/') ? path : path + '/') +end + + +class ConfigTable + + include Enumerable + + def initialize(rbconfig) + @rbconfig = rbconfig + @items = [] + @table = {} + # options + @install_prefix = nil + @config_opt = nil + @verbose = true + @no_harm = false + end + + attr_accessor :install_prefix + attr_accessor :config_opt + + attr_writer :verbose + + def verbose? + @verbose + end + + attr_writer :no_harm + + def no_harm? + @no_harm + end + + def [](key) + lookup(key).resolve(self) + end + + def []=(key, val) + lookup(key).set val + end + + def names + @items.map {|i| i.name } + end + + def each(&block) + @items.each(&block) + end + + def key?(name) + @table.key?(name) + end + + def lookup(name) + @table[name] or setup_rb_error "no such config item: #{name}" + end + + def add(item) + @items.push item + @table[item.name] = item + end + + def remove(name) + item = lookup(name) + @items.delete_if {|i| i.name == name } + @table.delete_if {|name, i| i.name == name } + item + end + + def load_script(path, inst = nil) + if File.file?(path) + MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path + end + end + + def savefile + '.config' + end + + def load_savefile + begin + File.foreach(savefile()) do |line| + k, v = *line.split(/=/, 2) + self[k] = v.strip + end + rescue Errno::ENOENT + setup_rb_error $!.message + "\n#{File.basename($0)} config first" + end + end + + def save + @items.each {|i| i.value } + File.open(savefile(), 'w') {|f| + @items.each do |i| + f.printf "%s=%s\n", i.name, i.value if i.value? and i.value + end + } + end + + def load_standard_entries + standard_entries(@rbconfig).each do |ent| + add ent + end + end + + def standard_entries(rbconfig) + c = rbconfig + + rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) + + major = c['MAJOR'].to_i + minor = c['MINOR'].to_i + teeny = c['TEENY'].to_i + version = "#{major}.#{minor}" + + # ruby ver. >= 1.4.4? + newpath_p = ((major >= 2) or + ((major == 1) and + ((minor >= 5) or + ((minor == 4) and (teeny >= 4))))) + + if c['rubylibdir'] + # V > 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = c['rubylibdir'] + librubyverarch = c['archdir'] + siteruby = c['sitedir'] + siterubyver = c['sitelibdir'] + siterubyverarch = c['sitearchdir'] + elsif newpath_p + # 1.4.4 <= V <= 1.6.3 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = c['sitedir'] + siterubyver = "$siteruby/#{version}" + siterubyverarch = "$siterubyver/#{c['arch']}" + else + # V < 1.4.4 + libruby = "#{c['prefix']}/lib/ruby" + librubyver = "#{c['prefix']}/lib/ruby/#{version}" + librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" + siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" + siterubyver = siteruby + siterubyverarch = "$siterubyver/#{c['arch']}" + end + parameterize = lambda {|path| + path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') + } + + if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } + makeprog = arg.sub(/'/, '').split(/=/, 2)[1] + else + makeprog = 'make' + end + + [ + ExecItem.new('installdirs', 'std/site/home', + 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ + {|val, table| + case val + when 'std' + table['rbdir'] = '$librubyver' + table['sodir'] = '$librubyverarch' + when 'site' + table['rbdir'] = '$siterubyver' + table['sodir'] = '$siterubyverarch' + when 'home' + setup_rb_error '$HOME was not set' unless ENV['HOME'] + table['prefix'] = ENV['HOME'] + table['rbdir'] = '$libdir/ruby' + table['sodir'] = '$libdir/ruby' + end + }, + PathItem.new('prefix', 'path', c['prefix'], + 'path prefix of target environment'), + PathItem.new('bindir', 'path', parameterize.call(c['bindir']), + 'the directory for commands'), + PathItem.new('libdir', 'path', parameterize.call(c['libdir']), + 'the directory for libraries'), + PathItem.new('datadir', 'path', parameterize.call(c['datadir']), + 'the directory for shared data'), + PathItem.new('mandir', 'path', parameterize.call(c['mandir']), + 'the directory for man pages'), + PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), + 'the directory for system configuration files'), + PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), + 'the directory for local state data'), + PathItem.new('libruby', 'path', libruby, + 'the directory for ruby libraries'), + PathItem.new('librubyver', 'path', librubyver, + 'the directory for standard ruby libraries'), + PathItem.new('librubyverarch', 'path', librubyverarch, + 'the directory for standard ruby extensions'), + PathItem.new('siteruby', 'path', siteruby, + 'the directory for version-independent aux ruby libraries'), + PathItem.new('siterubyver', 'path', siterubyver, + 'the directory for aux ruby libraries'), + PathItem.new('siterubyverarch', 'path', siterubyverarch, + 'the directory for aux ruby binaries'), + PathItem.new('rbdir', 'path', '$siterubyver', + 'the directory for ruby scripts'), + PathItem.new('sodir', 'path', '$siterubyverarch', + 'the directory for ruby extentions'), + PathItem.new('rubypath', 'path', rubypath, + 'the path to set to #! line'), + ProgramItem.new('rubyprog', 'name', rubypath, + 'the ruby program using for installation'), + ProgramItem.new('makeprog', 'name', makeprog, + 'the make program to compile ruby extentions'), + SelectItem.new('shebang', 'all/ruby/never', 'ruby', + 'shebang line (#!) editing mode'), + BoolItem.new('without-ext', 'yes/no', 'no', + 'does not compile/install ruby extentions') + ] + end + private :standard_entries + + def load_multipackage_entries + multipackage_entries().each do |ent| + add ent + end + end + + def multipackage_entries + [ + PackageSelectionItem.new('with', 'name,name...', '', 'ALL', + 'package names that you want to install'), + PackageSelectionItem.new('without', 'name,name...', '', 'NONE', + 'package names that you do not want to install') + ] + end + private :multipackage_entries + + ALIASES = { + 'std-ruby' => 'librubyver', + 'stdruby' => 'librubyver', + 'rubylibdir' => 'librubyver', + 'archdir' => 'librubyverarch', + 'site-ruby-common' => 'siteruby', # For backward compatibility + 'site-ruby' => 'siterubyver', # For backward compatibility + 'bin-dir' => 'bindir', + 'bin-dir' => 'bindir', + 'rb-dir' => 'rbdir', + 'so-dir' => 'sodir', + 'data-dir' => 'datadir', + 'ruby-path' => 'rubypath', + 'ruby-prog' => 'rubyprog', + 'ruby' => 'rubyprog', + 'make-prog' => 'makeprog', + 'make' => 'makeprog' + } + + def fixup + ALIASES.each do |ali, name| + @table[ali] = @table[name] + end + end + + def options_re + /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ + end + + def parse_opt(opt) + m = options_re().match(opt) or setup_rb_error "config: unknown option #{opt}" + m.to_a[1,2] + end + + def dllext + @rbconfig['DLEXT'] + end + + def value_config?(name) + lookup(name).value? + end + + class Item + def initialize(name, template, default, desc) + @name = name.freeze + @template = template + @value = default + @default = default + @description = desc + end + + attr_reader :name + attr_reader :description + + attr_accessor :default + alias help_default default + + def help_opt + "--#{@name}=#{@template}" + end + + def value? + true + end + + def value + @value + end + + def resolve(table) + @value.gsub(%r<\$([^/]+)>) { table[$1] } + end + + def set(val) + @value = check(val) + end + + private + + def check(val) + setup_rb_error "config: --#{name} requires argument" unless val + val + end + end + + class BoolItem < Item + def config_type + 'bool' + end + + def help_opt + "--#{@name}" + end + + private + + def check(val) + return 'yes' unless val + case val + when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' + when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' + else + setup_rb_error "config: --#{@name} accepts only yes/no for argument" + end + end + end + + class PathItem < Item + def config_type + 'path' + end + + private + + def check(path) + setup_rb_error "config: --#{@name} requires argument" unless path + path[0,1] == '$' ? path : File.expand_path(path) + end + end + + class ProgramItem < Item + def config_type + 'program' + end + end + + class SelectItem < Item + def initialize(name, selection, default, desc) + super + @ok = selection.split('/') + end + + def config_type + 'select' + end + + private + + def check(val) + unless @ok.include?(val.strip) + setup_rb_error "config: use --#{@name}=#{@template} (#{val})" + end + val.strip + end + end + + class ExecItem < Item + def initialize(name, selection, desc, &block) + super name, selection, nil, desc + @ok = selection.split('/') + @action = block + end + + def config_type + 'exec' + end + + def value? + false + end + + def resolve(table) + setup_rb_error "$#{name()} wrongly used as option value" + end + + undef set + + def evaluate(val, table) + v = val.strip.downcase + unless @ok.include?(v) + setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" + end + @action.call v, table + end + end + + class PackageSelectionItem < Item + def initialize(name, template, default, help_default, desc) + super name, template, default, desc + @help_default = help_default + end + + attr_reader :help_default + + def config_type + 'package' + end + + private + + def check(val) + unless File.dir?("packages/#{val}") + setup_rb_error "config: no such package: #{val}" + end + val + end + end + + class MetaConfigEnvironment + def initialize(config, installer) + @config = config + @installer = installer + end + + def config_names + @config.names + end + + def config?(name) + @config.key?(name) + end + + def bool_config?(name) + @config.lookup(name).config_type == 'bool' + end + + def path_config?(name) + @config.lookup(name).config_type == 'path' + end + + def value_config?(name) + @config.lookup(name).config_type != 'exec' + end + + def add_config(item) + @config.add item + end + + def add_bool_config(name, default, desc) + @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) + end + + def add_path_config(name, default, desc) + @config.add PathItem.new(name, 'path', default, desc) + end + + def set_config_default(name, default) + @config.lookup(name).default = default + end + + def remove_config(name) + @config.remove(name) + end + + # For only multipackage + def packages + raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer + @installer.packages + end + + # For only multipackage + def declare_packages(list) + raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer + @installer.packages = list + end + end + +end # class ConfigTable + + +# This module requires: #verbose?, #no_harm? +module FileOperations + + def mkdir_p(dirname, prefix = nil) + dirname = prefix + File.expand_path(dirname) if prefix + $stderr.puts "mkdir -p #{dirname}" if verbose? + return if no_harm? + + # Does not check '/', it's too abnormal. + dirs = File.expand_path(dirname).split(%r<(?=/)>) + if /\A[a-z]:\z/i =~ dirs[0] + disk = dirs.shift + dirs[0] = disk + dirs[0] + end + dirs.each_index do |idx| + path = dirs[0..idx].join('') + Dir.mkdir path unless File.dir?(path) + end + end + + def rm_f(path) + $stderr.puts "rm -f #{path}" if verbose? + return if no_harm? + force_remove_file path + end + + def rm_rf(path) + $stderr.puts "rm -rf #{path}" if verbose? + return if no_harm? + remove_tree path + end + + def remove_tree(path) + if File.symlink?(path) + remove_file path + elsif File.dir?(path) + remove_tree0 path + else + force_remove_file path + end + end + + def remove_tree0(path) + Dir.foreach(path) do |ent| + next if ent == '.' + next if ent == '..' + entpath = "#{path}/#{ent}" + if File.symlink?(entpath) + remove_file entpath + elsif File.dir?(entpath) + remove_tree0 entpath + else + force_remove_file entpath + end + end + begin + Dir.rmdir path + rescue Errno::ENOTEMPTY + # directory may not be empty + end + end + + def move_file(src, dest) + force_remove_file dest + begin + File.rename src, dest + rescue + File.open(dest, 'wb') {|f| + f.write File.binread(src) + } + File.chmod File.stat(src).mode, dest + File.unlink src + end + end + + def force_remove_file(path) + begin + remove_file path + rescue + end + end + + def remove_file(path) + File.chmod 0777, path + File.unlink path + end + + def install(from, dest, mode, prefix = nil) + $stderr.puts "install #{from} #{dest}" if verbose? + return if no_harm? + + realdest = prefix ? prefix + File.expand_path(dest) : dest + realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) + str = File.binread(from) + if diff?(str, realdest) + verbose_off { + rm_f realdest if File.exist?(realdest) + } + File.open(realdest, 'wb') {|f| + f.write str + } + File.chmod mode, realdest + + File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| + if prefix + f.puts realdest.sub(prefix, '') + else + f.puts realdest + end + } + end + end + + def diff?(new_content, path) + return true unless File.exist?(path) + new_content != File.binread(path) + end + + def command(*args) + $stderr.puts args.join(' ') if verbose? + system(*args) or raise RuntimeError, + "system(#{args.map{|a| a.inspect }.join(' ')}) failed" + end + + def ruby(*args) + command config('rubyprog'), *args + end + + def make(task = nil) + command(*[config('makeprog'), task].compact) + end + + def extdir?(dir) + File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") + end + + def files_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.file?("#{dir}/#{ent}") } + } + end + + DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) + + def directories_of(dir) + Dir.open(dir) {|d| + return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT + } + end + +end + + +# This module requires: #srcdir_root, #objdir_root, #relpath +module HookScriptAPI + + def get_config(key) + @config[key] + end + + alias config get_config + + # obsolete: use metaconfig to change configuration + def set_config(key, val) + @config[key] = val + end + + # + # srcdir/objdir (works only in the package directory) + # + + def curr_srcdir + "#{srcdir_root()}/#{relpath()}" + end + + def curr_objdir + "#{objdir_root()}/#{relpath()}" + end + + def srcfile(path) + "#{curr_srcdir()}/#{path}" + end + + def srcexist?(path) + File.exist?(srcfile(path)) + end + + def srcdirectory?(path) + File.dir?(srcfile(path)) + end + + def srcfile?(path) + File.file?(srcfile(path)) + end + + def srcentries(path = '.') + Dir.open("#{curr_srcdir()}/#{path}") {|d| + return d.to_a - %w(. ..) + } + end + + def srcfiles(path = '.') + srcentries(path).select {|fname| + File.file?(File.join(curr_srcdir(), path, fname)) + } + end + + def srcdirectories(path = '.') + srcentries(path).select {|fname| + File.dir?(File.join(curr_srcdir(), path, fname)) + } + end + +end + + +class ToplevelInstaller + + Version = '3.4.1' + Copyright = 'Copyright (c) 2000-2006 Minero Aoki' + + TASKS = [ + [ 'all', 'do config, setup, then install' ], + [ 'config', 'saves your configurations' ], + [ 'show', 'shows current configuration' ], + [ 'setup', 'compiles ruby extentions and others' ], + [ 'install', 'installs files' ], + [ 'test', 'run all tests in test/' ], + [ 'clean', "does `make clean' for each extention" ], + [ 'distclean',"does `make distclean' for each extention" ] + ] + + def ToplevelInstaller.invoke + config = ConfigTable.new(load_rbconfig()) + config.load_standard_entries + config.load_multipackage_entries if multipackage? + config.fixup + klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) + klass.new(File.dirname($0), config).invoke + end + + def ToplevelInstaller.multipackage? + File.dir?(File.dirname($0) + '/packages') + end + + def ToplevelInstaller.load_rbconfig + if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } + ARGV.delete(arg) + load File.expand_path(arg.split(/=/, 2)[1]) + $".push 'rbconfig.rb' + else + require 'rbconfig' + end + ::Config::CONFIG + end + + def initialize(ardir_root, config) + @ardir = File.expand_path(ardir_root) + @config = config + # cache + @valid_task_re = nil + end + + def config(key) + @config[key] + end + + def inspect + "#<#{self.class} #{__id__()}>" + end + + def invoke + run_metaconfigs + case task = parsearg_global() + when nil, 'all' + parsearg_config + init_installers + exec_config + exec_setup + exec_install + else + case task + when 'config', 'test' + ; + when 'clean', 'distclean' + @config.load_savefile if File.exist?(@config.savefile) + else + @config.load_savefile + end + __send__ "parsearg_#{task}" + init_installers + __send__ "exec_#{task}" + end + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig" + end + + def init_installers + @installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + # + # Hook Script API bases + # + + def srcdir_root + @ardir + end + + def objdir_root + '.' + end + + def relpath + '.' + end + + # + # Option Parsing + # + + def parsearg_global + while arg = ARGV.shift + case arg + when /\A\w+\z/ + setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) + return arg + when '-q', '--quiet' + @config.verbose = false + when '--verbose' + @config.verbose = true + when '--help' + print_usage $stdout + exit 0 + when '--version' + puts "#{File.basename($0)} version #{Version}" + exit 0 + when '--copyright' + puts Copyright + exit 0 + else + setup_rb_error "unknown global option '#{arg}'" + end + end + nil + end + + def valid_task?(t) + valid_task_re() =~ t + end + + def valid_task_re + @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ + end + + def parsearg_no_options + unless ARGV.empty? + task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) + setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" + end + end + + alias parsearg_show parsearg_no_options + alias parsearg_setup parsearg_no_options + alias parsearg_test parsearg_no_options + alias parsearg_clean parsearg_no_options + alias parsearg_distclean parsearg_no_options + + def parsearg_config + evalopt = [] + set = [] + @config.config_opt = [] + while i = ARGV.shift + if /\A--?\z/ =~ i + @config.config_opt = ARGV.dup + break + end + name, value = *@config.parse_opt(i) + if @config.value_config?(name) + @config[name] = value + else + evalopt.push [name, value] + end + set.push name + end + evalopt.each do |name, value| + @config.lookup(name).evaluate value, @config + end + # Check if configuration is valid + set.each do |n| + @config[n] if @config.value_config?(n) + end + end + + def parsearg_install + @config.no_harm = false + @config.install_prefix = '' + while a = ARGV.shift + case a + when '--no-harm' + @config.no_harm = true + when /\A--prefix=/ + path = a.split(/=/, 2)[1] + path = File.expand_path(path) unless path[0,1] == '/' + @config.install_prefix = path + else + setup_rb_error "install: unknown option #{a}" + end + end + end + + def print_usage(out) + out.puts 'Typical Installation Procedure:' + out.puts " $ ruby #{File.basename $0} config" + out.puts " $ ruby #{File.basename $0} setup" + out.puts " # ruby #{File.basename $0} install (may require root privilege)" + out.puts + out.puts 'Detailed Usage:' + out.puts " ruby #{File.basename $0} " + out.puts " ruby #{File.basename $0} [] []" + + fmt = " %-24s %s\n" + out.puts + out.puts 'Global options:' + out.printf fmt, '-q,--quiet', 'suppress message outputs' + out.printf fmt, ' --verbose', 'output messages verbosely' + out.printf fmt, ' --help', 'print this message' + out.printf fmt, ' --version', 'print version and quit' + out.printf fmt, ' --copyright', 'print copyright and quit' + out.puts + out.puts 'Tasks:' + TASKS.each do |name, desc| + out.printf fmt, name, desc + end + + fmt = " %-24s %s [%s]\n" + out.puts + out.puts 'Options for CONFIG or ALL:' + @config.each do |item| + out.printf fmt, item.help_opt, item.description, item.help_default + end + out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" + out.puts + out.puts 'Options for INSTALL:' + out.printf fmt, '--no-harm', 'only display what to do if given', 'off' + out.printf fmt, '--prefix=path', 'install path prefix', '' + out.puts + end + + # + # Task Handlers + # + + def exec_config + @installer.exec_config + @config.save # must be final + end + + def exec_setup + @installer.exec_setup + end + + def exec_install + @installer.exec_install + end + + def exec_test + @installer.exec_test + end + + def exec_show + @config.each do |i| + printf "%-20s %s\n", i.name, i.value if i.value? + end + end + + def exec_clean + @installer.exec_clean + end + + def exec_distclean + @installer.exec_distclean + end + +end # class ToplevelInstaller + + +class ToplevelInstallerMulti < ToplevelInstaller + + include FileOperations + + def initialize(ardir_root, config) + super + @packages = directories_of("#{@ardir}/packages") + raise 'no package exists' if @packages.empty? + @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) + end + + def run_metaconfigs + @config.load_script "#{@ardir}/metaconfig", self + @packages.each do |name| + @config.load_script "#{@ardir}/packages/#{name}/metaconfig" + end + end + + attr_reader :packages + + def packages=(list) + raise 'package list is empty' if list.empty? + list.each do |name| + raise "directory packages/#{name} does not exist"\ + unless File.dir?("#{@ardir}/packages/#{name}") + end + @packages = list + end + + def init_installers + @installers = {} + @packages.each do |pack| + @installers[pack] = Installer.new(@config, + "#{@ardir}/packages/#{pack}", + "packages/#{pack}") + end + with = extract_selection(config('with')) + without = extract_selection(config('without')) + @selected = @installers.keys.select {|name| + (with.empty? or with.include?(name)) \ + and not without.include?(name) + } + end + + def extract_selection(list) + a = list.split(/,/) + a.each do |name| + setup_rb_error "no such package: #{name}" unless @installers.key?(name) + end + a + end + + def print_usage(f) + super + f.puts 'Inluded packages:' + f.puts ' ' + @packages.sort.join(' ') + f.puts + end + + # + # Task Handlers + # + + def exec_config + run_hook 'pre-config' + each_selected_installers {|inst| inst.exec_config } + run_hook 'post-config' + @config.save # must be final + end + + def exec_setup + run_hook 'pre-setup' + each_selected_installers {|inst| inst.exec_setup } + run_hook 'post-setup' + end + + def exec_install + run_hook 'pre-install' + each_selected_installers {|inst| inst.exec_install } + run_hook 'post-install' + end + + def exec_test + run_hook 'pre-test' + each_selected_installers {|inst| inst.exec_test } + run_hook 'post-test' + end + + def exec_clean + rm_f @config.savefile + run_hook 'pre-clean' + each_selected_installers {|inst| inst.exec_clean } + run_hook 'post-clean' + end + + def exec_distclean + rm_f @config.savefile + run_hook 'pre-distclean' + each_selected_installers {|inst| inst.exec_distclean } + run_hook 'post-distclean' + end + + # + # lib + # + + def each_selected_installers + Dir.mkdir 'packages' unless File.dir?('packages') + @selected.each do |pack| + $stderr.puts "Processing the package `#{pack}' ..." if verbose? + Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") + Dir.chdir "packages/#{pack}" + yield @installers[pack] + Dir.chdir '../..' + end + end + + def run_hook(id) + @root_installer.run_hook id + end + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + +end # class ToplevelInstallerMulti + + +class Installer + + FILETYPES = %w( bin lib ext data conf man ) + + include FileOperations + include HookScriptAPI + + def initialize(config, srcroot, objroot) + @config = config + @srcdir = File.expand_path(srcroot) + @objdir = File.expand_path(objroot) + @currdir = '.' + end + + def inspect + "#<#{self.class} #{File.basename(@srcdir)}>" + end + + def noop(rel) + end + + # + # Hook Script API base methods + # + + def srcdir_root + @srcdir + end + + def objdir_root + @objdir + end + + def relpath + @currdir + end + + # + # Config Access + # + + # module FileOperations requires this + def verbose? + @config.verbose? + end + + # module FileOperations requires this + def no_harm? + @config.no_harm? + end + + def verbose_off + begin + save, @config.verbose = @config.verbose?, false + yield + ensure + @config.verbose = save + end + end + + # + # TASK config + # + + def exec_config + exec_task_traverse 'config' + end + + alias config_dir_bin noop + alias config_dir_lib noop + + def config_dir_ext(rel) + extconf if extdir?(curr_srcdir()) + end + + alias config_dir_data noop + alias config_dir_conf noop + alias config_dir_man noop + + def extconf + ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt + end + + # + # TASK setup + # + + def exec_setup + exec_task_traverse 'setup' + end + + def setup_dir_bin(rel) + files_of(curr_srcdir()).each do |fname| + update_shebang_line "#{curr_srcdir()}/#{fname}" + end + end + + alias setup_dir_lib noop + + def setup_dir_ext(rel) + make if extdir?(curr_srcdir()) + end + + alias setup_dir_data noop + alias setup_dir_conf noop + alias setup_dir_man noop + + def update_shebang_line(path) + return if no_harm? + return if config('shebang') == 'never' + old = Shebang.load(path) + if old + $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 + new = new_shebang(old) + return if new.to_s == old.to_s + else + return unless config('shebang') == 'all' + new = Shebang.new(config('rubypath')) + end + $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? + open_atomic_writer(path) {|output| + File.open(path, 'rb') {|f| + f.gets if old # discard + output.puts new.to_s + output.print f.read + } + } + end + + def new_shebang(old) + if /\Aruby/ =~ File.basename(old.cmd) + Shebang.new(config('rubypath'), old.args) + elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' + Shebang.new(config('rubypath'), old.args[1..-1]) + else + return old unless config('shebang') == 'all' + Shebang.new(config('rubypath')) + end + end + + def open_atomic_writer(path, &block) + tmpfile = File.basename(path) + '.tmp' + begin + File.open(tmpfile, 'wb', &block) + File.rename tmpfile, File.basename(path) + ensure + File.unlink tmpfile if File.exist?(tmpfile) + end + end + + class Shebang + def Shebang.load(path) + line = nil + File.open(path) {|f| + line = f.gets + } + return nil unless /\A#!/ =~ line + parse(line) + end + + def Shebang.parse(line) + cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') + new(cmd, args) + end + + def initialize(cmd, args = []) + @cmd = cmd + @args = args + end + + attr_reader :cmd + attr_reader :args + + def to_s + "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") + end + end + + # + # TASK install + # + + def exec_install + rm_f 'InstalledFiles' + exec_task_traverse 'install' + end + + def install_dir_bin(rel) + install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755, strip_ext? + end + + def strip_ext? + /mswin|mingw/ !~ RUBY_PLATFORM + end + + def install_dir_lib(rel) + install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 + end + + def install_dir_ext(rel) + return unless extdir?(curr_srcdir()) + install_files rubyextentions('.'), + "#{config('sodir')}/#{File.dirname(rel)}", + 0555 + end + + def install_dir_data(rel) + install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 + end + + def install_dir_conf(rel) + # FIXME: should not remove current config files + # (rename previous file to .old/.org) + install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 + end + + def install_dir_man(rel) + install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 + end + + def install_files(list, dest, mode, stripext = false) + mkdir_p dest, @config.install_prefix + list.each do |fname| + if stripext + install fname, "#{dest}/#{File.basename(fname, '.*')}", + mode, @config.install_prefix + else + install fname, dest, mode, @config.install_prefix + end + end + end + + def libfiles + glob_reject(%w(*.y *.output), targetfiles()) + end + + def rubyextentions(dir) + ents = glob_select("*.#{@config.dllext}", targetfiles()) + if ents.empty? + setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" + end + ents + end + + def targetfiles + mapdir(existfiles() - hookfiles()) + end + + def mapdir(ents) + ents.map {|ent| + if File.exist?(ent) + then ent # objdir + else "#{curr_srcdir()}/#{ent}" # srcdir + end + } + end + + # picked up many entries from cvs-1.11.1/src/ignore.c + JUNK_FILES = %w( + core RCSLOG tags TAGS .make.state + .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb + *~ *.old *.bak *.BAK *.orig *.rej _$* *$ + + *.org *.in .* + ) + + def existfiles + glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) + end + + def hookfiles + %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| + %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) } + }.flatten + end + + def glob_select(pat, ents) + re = globs2re([pat]) + ents.select {|ent| re =~ ent } + end + + def glob_reject(pats, ents) + re = globs2re(pats) + ents.reject {|ent| re =~ ent } + end + + GLOB2REGEX = { + '.' => '\.', + '$' => '\$', + '#' => '\#', + '*' => '.*' + } + + def globs2re(pats) + /\A(?:#{ + pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') + })\z/ + end + + # + # TASK test + # + + TESTDIR = 'test' + + def exec_test + unless File.directory?('test') + $stderr.puts 'no test in this package' if verbose? + return + end + $stderr.puts 'Running tests...' if verbose? + begin + require 'test/unit' + rescue LoadError + setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' + end + runner = Test::Unit::AutoRunner.new(true) + runner.to_run << TESTDIR + runner.run + end + + # + # TASK clean + # + + def exec_clean + exec_task_traverse 'clean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias clean_dir_bin noop + alias clean_dir_lib noop + alias clean_dir_data noop + alias clean_dir_conf noop + alias clean_dir_man noop + + def clean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'clean' if File.file?('Makefile') + end + + # + # TASK distclean + # + + def exec_distclean + exec_task_traverse 'distclean' + rm_f @config.savefile + rm_f 'InstalledFiles' + end + + alias distclean_dir_bin noop + alias distclean_dir_lib noop + + def distclean_dir_ext(rel) + return unless extdir?(curr_srcdir()) + make 'distclean' if File.file?('Makefile') + end + + alias distclean_dir_data noop + alias distclean_dir_conf noop + alias distclean_dir_man noop + + # + # Traversing + # + + def exec_task_traverse(task) + run_hook "pre-#{task}" + FILETYPES.each do |type| + if type == 'ext' and config('without-ext') == 'yes' + $stderr.puts 'skipping ext/* by user option' if verbose? + next + end + traverse task, type, "#{task}_dir_#{type}" + end + run_hook "post-#{task}" + end + + def traverse(task, rel, mid) + dive_into(rel) { + run_hook "pre-#{task}" + __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') + directories_of(curr_srcdir()).each do |d| + traverse task, "#{rel}/#{d}", mid + end + run_hook "post-#{task}" + } + end + + def dive_into(rel) + return unless File.dir?("#{@srcdir}/#{rel}") + + dir = File.basename(rel) + Dir.mkdir dir unless File.dir?(dir) + prevdir = Dir.pwd + Dir.chdir dir + $stderr.puts '---> ' + rel if verbose? + @currdir = rel + yield + Dir.chdir prevdir + $stderr.puts '<--- ' + rel if verbose? + @currdir = File.dirname(rel) + end + + def run_hook(id) + path = [ "#{curr_srcdir()}/#{id}", + "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } + return unless path + $stderr.puts "invoking hook script #{path}" if verbose? + begin + instance_eval File.read(path), path, 1 + rescue + raise if $DEBUG + setup_rb_error "hook #{path} failed:\n" + $!.message + end + end + +end # class Installer + + +class SetupError < StandardError; end + +def setup_rb_error(msg) + raise SetupError, msg +end + +if $0 == __FILE__ + begin + ToplevelInstaller.invoke + rescue SetupError + raise if $DEBUG + $stderr.puts $!.message + $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." + exit 1 + end +end diff --git a/tasks/csts.rake b/tasks/csts.rake new file mode 100644 index 0000000..be656e3 --- /dev/null +++ b/tasks/csts.rake @@ -0,0 +1,72 @@ + +# GLOBAL CONFIG +F_CHMOD = 0644 +D_CHMOD = 0755 + +# VERSION +RUBY_1_9 = RUBY_VERSION =~ /^1\.9/ +WIN = (RUBY_PLATFORM =~ /mswin|cygwin/) +SUDO = (WIN ? "" : "sudo" ) + +# EXTENSIONS +HAVE_EXT = File.directory? "#{EXT_DIR}" +EXT_CONF_FILES = FileList["#{EXT_DIR}/**/extconf.rb"] +EXT_SRC_FILES = FileList["#{EXT_DIR}/**/*.{c,h}"] +EXT_FILES = EXT_SRC_FILES + EXT_CONF_FILES + +# FILES +RAKE_FILES = FileList[RAKEFILE, PRJ_FILE, GEM_SPEC_FILE, SETUP_FILE, "#{RAKE_DIR}/*"] +BIN_FILES = FileList["#{BIN_DIR}/**/*"] +LIB_FILES = FileList["#{LIB_DIR}/**/*.rb"] +TEST_FILES = FileList["#{TEST_DIR}/**/test_*.rb"] +RDOC_FILES = FileList[README, CHANGES, LICENSE] + +# DIST FILES +DIST_FILES = FileList[] +DIST_FILES.include(RAKE_FILES) +DIST_FILES.include(BIN_FILES) +DIST_FILES.include(LIB_FILES) +DIST_FILES.include(TEST_FILES) +DIST_FILES.include(RDOC_FILES) +DIST_FILES.include(DATA_FILES) +DIST_FILES.include(EXT_FILES) if HAVE_EXT +DIST_FILES.include("#{RDOC_DIR}/**/*.{html,css}", 'man/*.[0-9]') +DIST_FILES.exclude('**/tmp_*', '**/*.tmp') + +# +CLEAN.include( 'README' ) if File.exist? 'README.rb' +CLEAN.include( 'CHANGES' ) if File.exist? 'CHANGES.rb' +CLEAN.include( FileList["#{EXT_DIR}/**/*.{o,so}","#{EXT_DIR}/**/Makefile"], '.config' ) if HAVE_EXT + +# LOADPATH +REQUIRE_PATHS = [LIB_DIR] +REQUIRE_PATHS << FileList["#{EXT_DIR}/**"] if HAVE_EXT +#$LOAD_PATH.concat REQUIRE_PATHS + +# C EXTENSIONS TASKS +if HAVE_EXT + CONFIG_OPTS = ENV['CONFIG'] + file_create '.config' do + ruby "setup.rb -q config #{CONFIG_OPTS}" + end + + desc 'Configure and make C extensions. The CONFIG variable is passed to \'setup.rb config\'' + task :make_ext => '.config' do + ruby "setup.rb -q setup" + end + + task :test_ext => :make_ext + desc 'Run test after making the extensions.' + task :test => :make_ext do + Rake::Task[:test_ext].invoke + end +end + +# CERTIFICATE +cert_dir = ENV['CERT_DIR'] ||= File.expand_path(File.join('~', '.gem')) +HAVE_CERT = File.readable?(File.join(cert_dir, 'gem-private_key.pem')) and File.readable?(File.join(cert_dir, 'gem-public_cert.pem')) +if HAVE_CERT + CERT_PRIVATE = File.join(cert_dir, 'gem-private_key.pem') + CERT_PUBLIC = File.join(cert_dir, 'gem-public_cert.pem') +end + diff --git a/tasks/gem.rake b/tasks/gem.rake new file mode 100644 index 0000000..f22f722 --- /dev/null +++ b/tasks/gem.rake @@ -0,0 +1,22 @@ +# GEM TASK +Rake::GemPackageTask.new(GEM_SPEC) { |pkg| + pkg.need_zip = true + pkg.need_tar = true + puts "Signed with #{CERT_PRIVATE}" if HAVE_CERT +} + +desc "if dir chmod #{D_CHMOD} if file chmod #{F_CHMOD}" +task :chmod =>RDOC_FILES do + Dir.glob('**/*'){ |p| + open(p){ |f| f.chmod(F_CHMOD) } if File.file? p + open(p){ |f| f.chmod(D_CHMOD) } if File.directory? p + } +end + +task :release => [:clobber, :chmod] do + puts "Preparing release of #{PROJECT_NAME} version #{PROJECT_VERSION}" + Rake::Task[:test].invoke +# Rake::Task[:rdoc].invoke + Rake::Task[:package].invoke +end + diff --git a/tasks/rcov.rake b/tasks/rcov.rake new file mode 100644 index 0000000..b1ad8bb --- /dev/null +++ b/tasks/rcov.rake @@ -0,0 +1,9 @@ + +require 'rcov/rcovtask' + +# RCOV TASK +Rcov::RcovTask.new do |t| + t.output_dir = RCOV_DIR + t.verbose = true + t.rcov_opts = [ "--charset utf8", "-T", "-i ^test", "-i ^bin", "-i ^lib", "-x ^/home", "-x ^#{ENV['GEM_HOME']}"] +end diff --git a/tasks/rdoc.rake b/tasks/rdoc.rake new file mode 100644 index 0000000..7ceaba1 --- /dev/null +++ b/tasks/rdoc.rake @@ -0,0 +1,17 @@ + +require 'rake/rdoctask' + +# RDOC +GENERAL_RDOC_OPTS = { + "--title" => "#{PROJECT_NAME} API documentation", + "--main" => README +} + +# RDOC TASK +Rake::RDocTask.new(:rdoc) do |t| + t.rdoc_files = RDOC_FILES + LIB_FILES + t.title = GENERAL_RDOC_OPTS['--title'] + t.main = GENERAL_RDOC_OPTS['--main'] + t.rdoc_dir = RDOC_DIR + t.options += [ "--inline-source", "--line-numbers" ] +end diff --git a/tasks/show.rake b/tasks/show.rake new file mode 100644 index 0000000..f653ba8 --- /dev/null +++ b/tasks/show.rake @@ -0,0 +1,21 @@ +# TASK SHOW +def enum_files( label, files=[] ) + puts "#{label}\t:" #{files.length==0 ? 'nil' : ''}" + files.each{ |f| puts "\t #{f}" } +end + +desc 'Show current configuration of this project' +task :show do + puts "built on #{GEM_SPEC.date.strftime('%d-%m-%Y')}" + puts "project\t: #{PROJECT_NAME} #{UNIX_NAME}-#{PROJECT_VERSION} [ #{PROJECT_SUMMARY} ]" + puts "owner\t: #{USER_NAME} [#{RUBYFORGE_USER}] #{USER_EMAIL}" + enum_files 'rake', RAKE_FILES + enum_files 'bin', BIN_FILES + enum_files 'lib', LIB_FILES + enum_files EXT_DIR, EXT_FILES + enum_files 'test', TEST_FILES + enum_files 'rdoc', RDOC_FILES + enum_files 'data', DATA_FILES + enum_files 'paths', REQUIRE_PATHS +end + diff --git a/tasks/stats.rake b/tasks/stats.rake new file mode 100644 index 0000000..19ffd8e --- /dev/null +++ b/tasks/stats.rake @@ -0,0 +1,25 @@ + +desc 'lines and words count of real ruby code ( no empty lines or comments )' +task :stats do + pattern = Regexp.new("^\s*(?:#.*)?$") +# count_proc = Proc.new do |path| +# Dir[path].collect { |f| File.open(f).readlines.reject { |l| l =~ pattern }.size }.inject(0) { |sum,n| sum+=n } +# end + stat_proc = Proc.new do |files,name| + lines=0 + words=0 + files.each { |fn| open(fn){ |f| f.each{ |line| + begin + lines += 1 + words += line.split(' ').size + end if not pattern.match(line) + } } } + puts "#{ format('%10s',name)} => #{format('%7d',lines)} code lines, #{format('%7d',words)} words." + end + stat_proc.call BIN_FILES, 'bin' + stat_proc.call LIB_FILES, 'lib' + stat_proc.call TEST_FILES, 'test' + stat_proc.call EXT_FILES, 'ext' + +end + diff --git a/tasks/test.rake b/tasks/test.rake new file mode 100644 index 0000000..149706d --- /dev/null +++ b/tasks/test.rake @@ -0,0 +1,12 @@ + +require 'rake/testtask' + +# TEST TASK +test_task_name = HAVE_EXT ? :test_ext : :test +Rake::TestTask.new( test_task_name ) do |t| + t.libs = REQUIRE_PATHS + t.test_files = TEST_FILES + t.verbose = false +end + +task :test_ext => :make_ext if HAVE_EXT diff --git a/tasks/todo.rake b/tasks/todo.rake new file mode 100644 index 0000000..430936d --- /dev/null +++ b/tasks/todo.rake @@ -0,0 +1,6 @@ + +require 'todotask' + +# TODO TASK +ToDoTask.new + diff --git a/test/mmapt.rb b/test/mmapt.rb deleted file mode 100644 index 80c64c2..0000000 --- a/test/mmapt.rb +++ /dev/null @@ -1,380 +0,0 @@ -#!/usr/bin/ruby -$LOAD_PATH.unshift *%w{.. . test} -require 'mmap' -require 'ftools' - -$pathmm = $LOAD_PATH.find {|p| File.exist?(p + "/mmap.c") } -unless $pathmm - $LOAD_PATH.each do |p| - p p - if p.gsub!(%r{/test\Z}, '/') && - File.exists?(p + '/mmap.c') - $pathmm = p - break - end - end -end -raise "unable to find mmap.c" unless $pathmm - -load "#{$pathmm}/test/runit_.rb" - -$mmap, $str = nil, nil - -Inh = defined?(RUNIT) ? RUNIT : Test::Unit - -$pathmm = $LOAD_PATH.find {|p| File.exist?(p + "/mmap.c") } -raise "unable to find mmap.c" unless $pathmm - -Dir.mkdir("#{$pathmm}/tmp") unless FileTest.directory?("#{$pathmm}/tmp") - -Dir["#{$pathmm}/tmp/*"].each do |f| - File.unlink(f) if FileTest.file?(f) -end - -class TestMmap < Inh::TestCase - - def internal_read - File.readlines("#{$pathmm}/tmp/mmap", nil)[0] - end - - def internal_init(io = false) - $mmap.unmap if $mmap - file = "#{$pathmm}/mmap.c" - File.syscopy file, "#{$pathmm}/tmp/mmap" - $str = internal_read - if io - assert_kind_of(Mmap, $mmap = Mmap.new(File.new("#{$pathmm}/tmp/mmap", "r+"), "rw"), "") - else - assert_kind_of(Mmap, $mmap = Mmap.new("#{$pathmm}/tmp/mmap", "rw"), "") - end - end - - def test_00_init - internal_init - assert_equal($mmap.length, $str.length, "") - end - - def test_01_aref - max = $str.size * 2 - 72.times do - ran1 = rand(max) - assert_equal($mmap[ran1], $str[ran1], ""); - assert_equal($mmap[-ran1], $str[-ran1], ""); - ran2 = rand(max) - assert_equal($mmap[ran1, ran2], $str[ran1, ran2], ""); - assert_equal($mmap[-ran1, ran2], $str[-ran1, ran2], ""); - assert_equal($mmap[ran1, -ran2], $str[ran1, -ran2], ""); - assert_equal($mmap[-ran1, -ran2], $str[-ran1, -ran2], ""); - assert_equal($mmap[ran1 .. ran2], $str[ran1 .. ran2], ""); - assert_equal($mmap[-ran1 .. ran2], $str[-ran1 .. ran2], ""); - assert_equal($mmap[ran1 .. -ran2], $str[ran1 .. -ran2], ""); - assert_equal($mmap[-ran1 .. -ran2], $str[-ran1 .. -ran2], ""); - end - assert_equal($mmap[/random/], $str[/random/], "") - assert_equal($mmap[/real/], $str[/real/], "") - assert_equal($mmap[/none/], $str[/none/], "") - end - - def internal_aset(a, b = nil, c = true) - access = if b - repl = '' - rand(12).times do - repl << (65 + rand(25)) - end - if c - "[a, b] = '#{repl}'" - else - "[a .. b] = '#{repl}'" - end - else - "[a] = #{(65 + rand(25))}" - end - begin - eval "$str#{access}" - rescue IndexError, RangeError - begin - eval "$mmap#{access}" - rescue IndexError, RangeError - else - flunk("*must* fail with IndexError") - end - else - eval "$mmap#{access}" - end - assert_equal($mmap.to_str, $str, "") - end - - def test_02_aset - $mmap[/...../] = "change it" - $str[/...../] = "change it" - assert_equal($mmap.to_str, $str, "aset regexp") - $mmap["ge i"] = "change it" - $str["ge i"] = "change it" - assert_equal($mmap.to_str, $str, "aset regexp") - max = $str.size * 2 - 72.times do - ran1 = rand(max) - internal_aset(ran1) - internal_aset(-ran1) - ran2 = rand(max) - internal_aset(ran1, ran2) - internal_aset(ran1, -ran2) - internal_aset(-ran1, ran2) - internal_aset(-ran1, -ran2) - internal_aset(ran1, ran2, false) - internal_aset(ran1, -ran2, false) - internal_aset(-ran1, ran2, false) - internal_aset(-ran1, -ran2, false) - end - internal_init - end - - def internal_slice(a, b = nil, c = true) - access = if b - if c - ".slice!(a, b)" - else - ".slice!(a .. b)" - end - else - ".slice!(a)" - end - begin - eval "$str#{access}" - rescue IndexError, RangeError - begin - eval "$mmap#{access}" - rescue IndexError, RangeError - else - flunk("*must* fail with IndexError") - end - else - eval "$mmap#{access}" - end - assert_equal($mmap.to_str, $str, "") - end - - def test_03_slice - max = $str.size * 2 - 72.times do - ran1 = rand(max) - internal_slice(ran1) - internal_slice(-ran1) - ran2 = rand(max) - internal_slice(ran1, ran2) - internal_slice(ran1, -ran2) - internal_slice(-ran1, ran2) - internal_slice(-ran1, -ran2) - internal_slice(ran1, ran2, false) - internal_slice(ran1, -ran2, false) - internal_slice(-ran1, ran2, false) - internal_slice(-ran1, -ran2, false) - end - internal_init - end - - def test_04_reg - assert_equal($mmap.scan(/include/), $str.scan(/include/), "") - assert_equal($mmap.index("rb_raise"), $str.index("rb_raise"), "") - assert_equal($mmap.rindex("rb_raise"), $str.rindex("rb_raise"), "") - assert_equal($mmap.index(/rb_raise/), $str.index(/rb_raise/), "") - assert_equal($mmap.rindex(/rb_raise/), $str.rindex(/rb_raise/), "") - ('a' .. 'z').each do |i| - assert_equal($mmap.index(i), $str.index(i), "") - assert_equal($mmap.rindex(i), $str.rindex(i), "") - assert_equal($mmap.index(i), $str.index(/#{i}/), "") - assert_equal($mmap.rindex(i), $str.rindex(/#{i}/), "") - end - $mmap.sub!(/GetMmap/, 'XXXX'); $str.sub!(/GetMmap/, 'XXXX') - assert_equal($mmap.to_str, $str, "") - $mmap.gsub!(/GetMmap/, 'XXXX'); $str.gsub!(/GetMmap/, 'XXXX') - assert_equal($mmap.to_str, $str, "") - $mmap.gsub!(/YYYY/, 'XXXX'); $str.gsub!(/YYYY/, 'XXXX') - assert_equal($mmap.to_str, $str, "") - assert_equal($mmap.split(/\w+/), $str.split(/\w+/), "") - assert_equal($mmap.split(/\W+/), $str.split(/\W+/), "") - assert_equal($mmap.crypt("abc"), $str.crypt("abc"), "") - internal_init - end - - def internal_modify idmod, *args - if res = $str.method(idmod)[*args] - assert_equal($mmap.method(idmod)[*args].to_str, res, "<#{idmod}>") - else - assert_equal($mmap.method(idmod)[*args], res, "<#{idmod}>") - end - end - - def test_05_modify - internal_modify(:reverse!) - internal_modify(:upcase!) - internal_modify(:downcase!) - internal_modify(:capitalize!) - internal_modify(:swapcase!) - internal_modify(:strip!) - internal_modify(:chop!) - internal_modify(:chomp!) - internal_modify(:squeeze!) - internal_modify(:tr!, 'abcdefghi', '123456789') - internal_modify(:tr_s!, 'jklmnopqr', '123456789') - internal_modify(:delete!, 'A-Z') - end - - def test_06_iterate - internal_init - mmap = []; $mmap.each {|l| mmap << l} - str = []; $str.each {|l| str << l} - assert_equal(mmap, str, "") - mmap = []; $mmap.each_byte {|l| mmap << l} - str = []; $str.each_byte {|l| str << l} - assert_equal(mmap, str, "") - end - - def test_07_concat - internal_init - [$mmap, $str].each {|l| l << "bc"; l << 12; l << "ab"} - assert_equal($mmap.to_str, $str, "<<") - assert_raises(TypeError) { $mmap << 456 } - end - - def test_08_extend - $mmap.extend(4096) - assert_equal($mmap.to_str, $str, "extend") - if $str.respond_to?(:insert) - 10.times do - pos = rand($mmap.size) - str = "XX" * rand(66) - $str.insert(pos, str) - $mmap.insert(pos, str) - assert_equal($mmap.to_str, $str, "insert") - end - end - end - - def test_09_msync - 3.times do |i| - [$mmap, $str].each {|l| l << "x" * 4096 } - str = internal_read - if str != $mmap.to_str - $mmap.msync - assert_equal($mmap.to_str, internal_read, "msync") - break - end - end - end - - def test_10_protect - assert_equal($mmap, $mmap.protect("w"), "protect") - assert_equal("a", $mmap[12] = "a", "affect") - $str[12] = "a" - assert_equal($mmap.to_str, $str, "protect") - assert_raises(TypeError) { $mmap << "a" } - assert_equal($mmap, $mmap.protect("r"), "protect") - assert_raises(TypeError) { $mmap[12] = "a" } - assert_raises(TypeError) { $mmap.protect("rw") } - end - - def test_11_anonymous - if defined?(Mmap::MAP_ANONYMOUS) - assert_kind_of(Mmap, $mmap = - Mmap.new(nil, "length" => 8192, "offset" => 12, - "increment" => 1024, "initialize" => " ")) - $str = " " * 8192 - 1024.times do - pos = rand(8192) - $mmap[pos] = $str[pos] = 32 + rand(64) - end - assert_equal($mmap.to_str, $str, "insert anonymous") - assert_raises(IndexError) { $mmap[12345] = "a" } - assert_raises(TypeError) { $mmap << "a" } - end - end - - def test_12_fileno - internal_init(true) - test_01_aref - $mmap[12] = "3"; $str[12] = "3" - assert_equal($mmap.to_str, $str, "insert io") - assert_equal(0, $mmap <=> $str, "cmp") - assert_raises(TypeError) { $mmap[12] = "ab" } - $mmap.freeze - if $str.respond_to?(:match) - assert_equal($str.match("rb_match_busy").offset(0), - $mmap.match("rb_match_busy").offset(0), "match") - assert_equal($str.match(/rb_../).offset(0), - $mmap.match(/rb_../).offset(0), "match") - assert_equal($str.match("rb_match_buzy"), - $mmap.match("rb_match_buzy"), "no match") - assert_equal($str =~ /rb_match_busy/, - $mmap =~ /rb_match_busy/, "match") - assert_equal($str =~ /rb_match_buzy/, - $mmap =~ /rb_match_buzy/, "no match") - end - assert_raises(TypeError) { $mmap[12] = "a" } - end - - def test_13_div - string = "azertyuiopqsdfghjklm" - assert_kind_of(Mmap, m0 = Mmap.new("#{$pathmm}/tmp/aa", "a"), "new a") - File.open("#{$pathmm}/tmp/bb", "w") {|f| f.puts "aaa" } - assert_kind_of(Mmap, m1 = Mmap.new("#{$pathmm}/tmp/bb", "w"), "new a") - assert_equal(true, m0.empty?, "empty") - assert_equal(true, m1.empty?, "empty") - assert_equal(m0, m0 << string, "<<") - assert_equal(m1, m1 << string, "<<") - assert_equal(false, m0.empty?, "empty") - assert_equal(false, m1.empty?, "empty") - assert_equal(true, m0 == m1, "==") - if string.respond_to?(:casecmp) - assert_equal(0, m0.casecmp(string.upcase), "casecmp") - assert_equal(0, m0.casecmp(m1), "casecmp") - end - assert_equal(true, m0 === m1, "===") - assert_equal(false, m0 === string, "===") - assert_equal(true, m0.eql?(m1), ".eql?") - assert_equal(true, m1.eql?(m0), ".eql?") - assert_equal(false, m1.eql?(string), ".eql?") - assert_equal(m0.hash, m1.hash, "hash") - assert_equal(true, m0.include?("azert"), "include") - assert_equal(false, m1.include?("aqert"), "include") - i = 0 - m0.scan(/./) {|c| assert_equal(c, string[i,1], "scan"); i += 1} - assert_nil(m0.munmap, "munmap") - assert_nil(m1.munmap, "munmap") - end - - def test_14_other - if File.exist?("#{$pathmm}/tmp/aa") - string = "azertyuiopqsdfghjklm" - assert_kind_of(Mmap, m0 = Mmap.new("#{$pathmm}/tmp/aa", "r"), "new r") - assert_equal(string, m0.to_str, "content") - assert_raises(TypeError) { m0[0] = 12 } - assert_raises(TypeError) { m0 << 12 } - assert_nil(m0.munmap, "munmap") - if defined?(Mmap::MAP_ANONYMOUS) - assert_raises(ArgumentError) { Mmap.new(nil, "w") } - assert_kind_of(Mmap, m0 = Mmap.new(nil, 12), "new w") - assert_equal(false, m0.empty?, "empty") - assert_equal("a", m0[0] = "a", "set") - assert_raises(TypeError) { m0 << 12 } - if defined?(Mmap::MADV_DONTNEED) - assert_nil(m0.advise(Mmap::MADV_DONTNEED), "advise") - assert_equal("a", m0[0,1], "get") - end - assert_equal(m0, m0.sub!(/./) { "y" }, "sub") - assert_equal(m0, m0.gsub!(/./) { "x" }, "gsub") - assert_equal("x" * 12, m0.to_str, "retrieve") - assert_equal("ab", m0[1..2] = "ab", "range") - assert_raises(TypeError) { m0[1..2] = "abc" } - assert_raises(ArgumentError) { m0.lock } - assert_raises(ArgumentError) { Mmap::lockall(0) } - assert_nil(m0.munmap, "munmap") - end - end - end -end - -if defined?(RUNIT) - RUNIT::CUI::TestRunner.run(TestMmap.suite) -end - diff --git a/test/runit_.rb b/test/runit_.rb deleted file mode 100644 index 639c149..0000000 --- a/test/runit_.rb +++ /dev/null @@ -1,45 +0,0 @@ -#:nodoc:all -begin - require 'test/unit' -rescue LoadError - require 'runit/testcase' - require 'runit/cui/testrunner' - - module RUNIT - module Assert - def assert_raises(error, message = nil) - begin - yield - rescue error - assert(true, message) - rescue - assert_fail("must fail with #{error} : #{string}") - else - assert_fail("*must* fail : #{string}") - end - end - def flunk(message = "") - assert_fail(message) - end - end - end -end - - -if RUBY_VERSION > "1.7" - class Array - alias indices select - end - class Hash - alias indexes select - end - module BDB - class Common - alias indexes select - end - - class Recnum - alias indices select - end - end -end diff --git a/test/test_mmap.rb b/test/test_mmap.rb new file mode 100644 index 0000000..908a28e --- /dev/null +++ b/test/test_mmap.rb @@ -0,0 +1,364 @@ +#!/usr/bin/ruby + +require 'mmap' +require 'ftools' +require 'test/unit' + +$pathmm = $LOAD_PATH.find {|p| File.exist?(p + "/mmap.c") } +raise "unable to find mmap.c" unless $pathmm + +TMP_PATH="#{$pathmm}/tmp" + +class TestMmap < Test::Unit::TestCase + def setup + Dir.mkdir(TMP_PATH) unless FileTest.directory?(TMP_PATH) + end + + def internal_read + File.readlines("#{TMP_PATH}/mmap", nil)[0] + end + + def internal_init(io = false) + $mmap.unmap if $mmap + file = "#{$pathmm}/mmap.c" + File.syscopy file, "#{TMP_PATH}/mmap" + $str = internal_read + if io + assert_kind_of(Mmap, $mmap = Mmap.new(File.new("#{TMP_PATH}/mmap", "r+"), "rw"), "") + else + assert_kind_of(Mmap, $mmap = Mmap.new("#{TMP_PATH}/mmap", "rw"), "") + end + end + + def test_00_init + internal_init + assert_equal($mmap.length, $str.length, "") + end + + def test_01_aref + max = $str.size * 2 + 72.times do + ran1 = rand(max) + assert_equal($mmap[ran1], $str[ran1], ""); + assert_equal($mmap[-ran1], $str[-ran1], ""); + ran2 = rand(max) + assert_equal($mmap[ran1, ran2], $str[ran1, ran2], ""); + assert_equal($mmap[-ran1, ran2], $str[-ran1, ran2], ""); + assert_equal($mmap[ran1, -ran2], $str[ran1, -ran2], ""); + assert_equal($mmap[-ran1, -ran2], $str[-ran1, -ran2], ""); + assert_equal($mmap[ran1 .. ran2], $str[ran1 .. ran2], ""); + assert_equal($mmap[-ran1 .. ran2], $str[-ran1 .. ran2], ""); + assert_equal($mmap[ran1 .. -ran2], $str[ran1 .. -ran2], ""); + assert_equal($mmap[-ran1 .. -ran2], $str[-ran1 .. -ran2], ""); + end + assert_equal($mmap[/random/], $str[/random/], "") + assert_equal($mmap[/real/], $str[/real/], "") + assert_equal($mmap[/none/], $str[/none/], "") + end + + def internal_aset(a, b = nil, c = true) + access = if b + repl = '' + rand(12).times do + repl << (65 + rand(25)) + end + if c + "[a, b] = '#{repl}'" + else + "[a .. b] = '#{repl}'" + end + else + "[a] = #{(65 + rand(25))}" + end + begin + eval "$str#{access}" + rescue IndexError, RangeError + begin + eval "$mmap#{access}" + rescue IndexError, RangeError + else + flunk("*must* fail with IndexError") + end + else + eval "$mmap#{access}" + end + assert_equal($mmap.to_str, $str, "") + end + + def test_02_aset + $mmap[/...../] = "change it" + $str[/...../] = "change it" + assert_equal($mmap.to_str, $str, "aset regexp") + $mmap["ge i"] = "change it" + $str["ge i"] = "change it" + assert_equal($mmap.to_str, $str, "aset regexp") + max = $str.size * 2 + 72.times do + ran1 = rand(max) + internal_aset(ran1) + internal_aset(-ran1) + ran2 = rand(max) + internal_aset(ran1, ran2) + internal_aset(ran1, -ran2) + internal_aset(-ran1, ran2) + internal_aset(-ran1, -ran2) + internal_aset(ran1, ran2, false) + internal_aset(ran1, -ran2, false) + internal_aset(-ran1, ran2, false) + internal_aset(-ran1, -ran2, false) + end + internal_init + end + + def internal_slice(a, b = nil, c = true) + access = if b + if c + ".slice!(a, b)" + else + ".slice!(a .. b)" + end + else + ".slice!(a)" + end + begin + eval "$str#{access}" + rescue IndexError, RangeError + begin + eval "$mmap#{access}" + rescue IndexError, RangeError + else + flunk("*must* fail with IndexError") + end + else + eval "$mmap#{access}" + end + assert_equal($mmap.to_str, $str, "") + end + + def test_03_slice + max = $str.size * 2 + 72.times do + ran1 = rand(max) + internal_slice(ran1) + internal_slice(-ran1) + ran2 = rand(max) + internal_slice(ran1, ran2) + internal_slice(ran1, -ran2) + internal_slice(-ran1, ran2) + internal_slice(-ran1, -ran2) + internal_slice(ran1, ran2, false) + internal_slice(ran1, -ran2, false) + internal_slice(-ran1, ran2, false) + internal_slice(-ran1, -ran2, false) + end + internal_init + end + + def test_04_reg + assert_equal($mmap.scan(/include/), $str.scan(/include/), "") + assert_equal($mmap.index("rb_raise"), $str.index("rb_raise"), "") + assert_equal($mmap.rindex("rb_raise"), $str.rindex("rb_raise"), "") + assert_equal($mmap.index(/rb_raise/), $str.index(/rb_raise/), "") + assert_equal($mmap.rindex(/rb_raise/), $str.rindex(/rb_raise/), "") + ('a' .. 'z').each do |i| + assert_equal($mmap.index(i), $str.index(i), "") + assert_equal($mmap.rindex(i), $str.rindex(i), "") + assert_equal($mmap.index(i), $str.index(/#{i}/), "") + assert_equal($mmap.rindex(i), $str.rindex(/#{i}/), "") + end + $mmap.sub!(/GetMmap/, 'XXXX'); $str.sub!(/GetMmap/, 'XXXX') + assert_equal($mmap.to_str, $str, "") + $mmap.gsub!(/GetMmap/, 'XXXX'); $str.gsub!(/GetMmap/, 'XXXX') + assert_equal($mmap.to_str, $str, "") + $mmap.gsub!(/YYYY/, 'XXXX'); $str.gsub!(/YYYY/, 'XXXX') + assert_equal($mmap.to_str, $str, "") + assert_equal($mmap.split(/\w+/), $str.split(/\w+/), "") + assert_equal($mmap.split(/\W+/), $str.split(/\W+/), "") + assert_equal($mmap.crypt("abc"), $str.crypt("abc"), "") + internal_init + end + + def internal_modify idmod, *args + if res = $str.method(idmod)[*args] + assert_equal($mmap.method(idmod)[*args].to_str, res, "<#{idmod}>") + else + assert_equal($mmap.method(idmod)[*args], res, "<#{idmod}>") + end + end + + def test_05_modify + internal_modify(:reverse!) + internal_modify(:upcase!) + internal_modify(:downcase!) + internal_modify(:capitalize!) + internal_modify(:swapcase!) + internal_modify(:strip!) + internal_modify(:chop!) + internal_modify(:chomp!) + internal_modify(:squeeze!) + internal_modify(:tr!, 'abcdefghi', '123456789') + internal_modify(:tr_s!, 'jklmnopqr', '123456789') + internal_modify(:delete!, 'A-Z') + end + + def test_06_iterate + internal_init + mmap = []; $mmap.each {|l| mmap << l} + str = []; $str.each {|l| str << l} + assert_equal(mmap, str, "") + mmap = []; $mmap.each_byte {|l| mmap << l} + str = []; $str.each_byte {|l| str << l} + assert_equal(mmap, str, "") + end + + def test_07_concat + internal_init + [$mmap, $str].each {|l| l << "bc"; l << 12; l << "ab"} + assert_equal($mmap.to_str, $str, "<<") + assert_raises(TypeError) { $mmap << 456 } + end + + def test_08_extend + $mmap.extend(4096) + assert_equal($mmap.to_str, $str, "extend") + if $str.respond_to?(:insert) + 10.times do + pos = rand($mmap.size) + str = "XX" * rand(66) + $str.insert(pos, str) + $mmap.insert(pos, str) + assert_equal($mmap.to_str, $str, "insert") + end + end + end + + def test_09_msync + 3.times do |i| + [$mmap, $str].each {|l| l << "x" * 4096 } + str = internal_read + if str != $mmap.to_str + $mmap.msync + assert_equal($mmap.to_str, internal_read, "msync") + break + end + end + end + + def test_10_protect + assert_equal($mmap, $mmap.protect("w"), "protect") + assert_equal("a", $mmap[12] = "a", "affect") + $str[12] = "a" + assert_equal($mmap.to_str, $str, "protect") + assert_raises(TypeError) { $mmap << "a" } + assert_equal($mmap, $mmap.protect("r"), "protect") + assert_raises(TypeError) { $mmap[12] = "a" } + assert_raises(TypeError) { $mmap.protect("rw") } + end + + def test_11_anonymous + if defined?(Mmap::MAP_ANONYMOUS) + assert_kind_of(Mmap, $mmap = + Mmap.new(nil, "length" => 8192, "offset" => 12, + "increment" => 1024, "initialize" => " ")) + $str = " " * 8192 + 1024.times do + pos = rand(8192) + $mmap[pos] = $str[pos] = 32 + rand(64) + end + assert_equal($mmap.to_str, $str, "insert anonymous") + assert_raises(IndexError) { $mmap[12345] = "a" } + assert_raises(TypeError) { $mmap << "a" } + end + end + + def test_12_fileno + internal_init(true) + test_01_aref + $mmap[12] = "3"; $str[12] = "3" + assert_equal($mmap.to_str, $str, "insert io") + assert_equal(0, $mmap <=> $str, "cmp") + assert_raises(TypeError) { $mmap[12] = "ab" } + $mmap.freeze + if $str.respond_to?(:match) + assert_equal($str.match("rb_match_busy").offset(0), + $mmap.match("rb_match_busy").offset(0), "match") + assert_equal($str.match(/rb_../).offset(0), + $mmap.match(/rb_../).offset(0), "match") + assert_equal($str.match("rb_match_buzy"), + $mmap.match("rb_match_buzy"), "no match") + assert_equal($str =~ /rb_match_busy/, + $mmap =~ /rb_match_busy/, "match") + assert_equal($str =~ /rb_match_buzy/, + $mmap =~ /rb_match_buzy/, "no match") + end + assert_raises(TypeError) { $mmap[12] = "a" } + end + + def test_13_div + string = "azertyuiopqsdfghjklm" + assert_kind_of(Mmap, m0 = Mmap.new("#{TMP_PATH}/aa", "a"), "new a") + File.open("#{TMP_PATH}/bb", "w") {|f| f.puts "aaa" } + assert_kind_of(Mmap, m1 = Mmap.new("#{TMP_PATH}/bb", "w"), "new a") + assert_equal(true, m0.empty?, "empty") + assert_equal(true, m1.empty?, "empty") + assert_equal(m0, m0 << string, "<<") + assert_equal(m1, m1 << string, "<<") + assert_equal(false, m0.empty?, "empty") + assert_equal(false, m1.empty?, "empty") + assert_equal(true, m0 == m1, "==") + if string.respond_to?(:casecmp) + assert_equal(0, m0.casecmp(string.upcase), "casecmp") + assert_equal(0, m0.casecmp(m1), "casecmp") + end + assert_equal(true, m0 === m1, "===") + assert_equal(false, m0 === string, "===") + assert_equal(true, m0.eql?(m1), ".eql?") + assert_equal(true, m1.eql?(m0), ".eql?") + assert_equal(false, m1.eql?(string), ".eql?") + assert_equal(m0.hash, m1.hash, "hash") + assert_equal(true, m0.include?("azert"), "include") + assert_equal(false, m1.include?("aqert"), "include") + i = 0 + m0.scan(/./) {|c| assert_equal(c, string[i,1], "scan"); i += 1} + assert_nil(m0.munmap, "munmap") + assert_nil(m1.munmap, "munmap") + end + + def test_14_other + if File.exist?("#{TMP_PATH}/aa") + string = "azertyuiopqsdfghjklm" + assert_kind_of(Mmap, m0 = Mmap.new("#{TMP_PATH}/aa", "r"), "new r") + assert_equal(string, m0.to_str, "content") + assert_raises(TypeError) { m0[0] = 12 } + assert_raises(TypeError) { m0 << 12 } + assert_nil(m0.munmap, "munmap") + if defined?(Mmap::MAP_ANONYMOUS) + assert_raises(ArgumentError) { Mmap.new(nil, "w") } + assert_kind_of(Mmap, m0 = Mmap.new(nil, 12), "new w") + assert_equal(false, m0.empty?, "empty") + assert_equal("a", m0[0] = "a", "set") + assert_raises(TypeError) { m0 << 12 } + if defined?(Mmap::MADV_DONTNEED) + assert_nil(m0.advise(Mmap::MADV_DONTNEED), "advise") + assert_equal("a", m0[0,1], "get") + end + assert_equal(m0, m0.sub!(/./) { "y" }, "sub") + assert_equal(m0, m0.gsub!(/./) { "x" }, "gsub") + assert_equal("x" * 12, m0.to_str, "retrieve") + assert_equal("ab", m0[1..2] = "ab", "range") + assert_raises(TypeError) { m0[1..2] = "abc" } + assert_raises(ArgumentError) { m0.lock } + assert_raises(ArgumentError) { Mmap::lockall(0) } + assert_nil(m0.munmap, "munmap") + end + end + end + + def test_999 + Dir["#{TMP_PATH}/*"].each do |f| + File.unlink(f) if FileTest.file?(f) + end + Dir.unlink TMP_PATH + end +end + diff --git a/todo.rb b/todo.rb new file mode 100644 index 0000000..8355e2e --- /dev/null +++ b/todo.rb @@ -0,0 +1,51 @@ + +begin + make = open("Makefile", "a") + make.puts "\ntest: $(DLLIB)" + Dir.foreach('test') do |x| + next if /^\./ =~ x || /(_\.rb|~)$/ =~ x + next if FileTest.directory?(x) + make.print "\truby test/#{x}\n" + end + if unknown + make.print <<-EOT + +unknown: $(DLLIB) +\t@echo "main() {}" > /tmp/a.c +\t$(CC) -static /tmp/a.c $(OBJS) $(CPPFLAGS) $(DLDFLAGS) $(LIBS) $(LOCAL_LIBS) +\t@-rm /tmp/a.c a.out + +EOT + end + make.print <<-EOT +%.html: %.rd +\trd2 $< > ${<:%.rd=%.html} + + EOT + make.print "HTML = mmap.html" + doc = Dir['doc/*.rd'] + doc.each {|x| make.print " \\\n\t#{x.sub(/\.rd$/, '.html')}" } + make.print "\n\nRDOC = doc/mmap.rb" + make.puts + make.print <<-EOF + +rdoc: doc/doc/index.html + +doc/doc/index.html: $(RDOC) +\t@-(cd doc; rdoc mmap.rb) + +ri: doc/mmap.rb +\t@-(cd doc; rdoc -r mmap.rb) + +ri-site: doc/mmap.rb +\t@-(cd doc; rdoc -R mmap.rb) + +rd2: html + +html: $(HTML) + + EOF +ensure + make.close +end + -- cgit v1.1-2-g2b99