/* * Copyright (c) 2011, Vicent Marti * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define RSTRING_NOT_MODIFIED #include #include "ruby.h" #ifdef HAVE_RUBY_ENCODING_H #include #else #define rb_enc_copy(dst, src) #endif #include "autolink.h" #include "buffer.h" static VALUE rb_cRinku; extern void upshtml_autolink( struct buf *ob, struct buf *text, unsigned int flags, const char *link_attr, void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload), void *payload); static void autolink_callback(struct buf *link_text, const struct buf *link, void *block) { VALUE rb_link, rb_link_text; rb_link = rb_str_new(link->data, link->size); rb_link_text = rb_funcall((VALUE)block, rb_intern("call"), 1, rb_link); Check_Type(rb_link_text, T_STRING); bufput(link_text, RSTRING_PTR(rb_link_text), RSTRING_LEN(rb_link_text)); } /* * Document-method: auto_link * * call-seq: * auto_link(text, mode=:all, link_attr=nil) * auto_link(text, mode=:all, link_attr=nil) { |link_text| ... } * * Parses a block of text looking for "safe" urls or email addresses, * and turns them into HTML links with the given attributes. * * NOTE: Currently the follow protocols are considered safe and are the * only ones that will be autolinked. * * http:// https:// ftp:// mailto:// * * Email addresses are also autolinked by default. URLs without a protocol * specifier but starting with 'www.' will also be autolinked, defaulting to * the 'http://' protocol. * * +text+ is a string in plain text or HTML markup. If the string is formatted in * HTML, Rinku is smart enough to skip the links that are already enclosed in * tags. * * +mode+ is a symbol, either :all, :urls or :email_addresses, which specifies which * kind of links will be auto-linked. * * +link_attr+ is a string containing the link attributes for each link that * will be generated. These attributes are not sanitized and will be include as-is * in each generated link, e.g. * * auto_link('http://www.pokemon.com', :all, 'target="_blank"') * # => 'http://www.pokemon.com' * * This string can be autogenerated from a hash using the Rails `tag_options` helper. * * +block+ The method takes an optional block argument. If a block is passed, it will * be yielded for each found link in the text, and its return value will be used instead * of the name of the link. E.g. * * auto_link('Check it out at http://www.pokemon.com') do |url| * "THE POKEMAN WEBSITEZ" * end * # => 'Check it out at THE POKEMAN WEBSITEZ' */ static VALUE rb_rinku_autolink(int argc, VALUE *argv, VALUE self) { VALUE result, rb_text, rb_mode, rb_html, rb_block; struct buf input_buf = {0, 0, 0, 0, 0}, *output_buf; int link_mode; const char *link_attr = NULL; ID mode_sym; rb_scan_args(argc, argv, "12&", &rb_text, &rb_mode, &rb_html, &rb_block); Check_Type(rb_text, T_STRING); if (!NIL_P(rb_mode)) { Check_Type(rb_mode, T_SYMBOL); mode_sym = SYM2ID(rb_mode); } else { mode_sym = rb_intern("all"); } if (!NIL_P(rb_html)) { Check_Type(rb_html, T_STRING); link_attr = RSTRING_PTR(rb_html); } input_buf.data = RSTRING_PTR(rb_text); input_buf.size = RSTRING_LEN(rb_text); output_buf = bufnew(128); if (mode_sym == rb_intern("all")) link_mode = AUTOLINK_ALL; else if (mode_sym == rb_intern("email_addresses")) link_mode = AUTOLINK_EMAILS; else if (mode_sym == rb_intern("urls")) link_mode = AUTOLINK_URLS; else rb_raise(rb_eTypeError, "Invalid linking mode (possible values are :all, :urls, :email_addresses)"); link_mode |= AUTOLINK_SANITIZE; if (RTEST(rb_block)) upshtml_autolink(output_buf, &input_buf, link_mode, link_attr, &autolink_callback, (void*)rb_block); else upshtml_autolink(output_buf, &input_buf, link_mode, link_attr, NULL, NULL); result = rb_str_new(output_buf->data, output_buf->size); bufrelease(output_buf); /* force the input encoding */ rb_enc_copy(result, rb_text); return result; } void Init_rinku() { rb_cRinku = rb_define_class("Rinku", rb_cObject); rb_define_singleton_method(rb_cRinku, "auto_link", rb_rinku_autolink, -1); }