Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial commit. Mandatory ASCII chicken: <")3
  • Loading branch information
vmg committed Jun 9, 2011
0 parents commit 96e7db3
Show file tree
Hide file tree
Showing 16 changed files with 1,365 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
@@ -0,0 +1,3 @@
[submodule "upskirt"]
path = upskirt
url = git://github.com/tanoku/upskirt.git
13 changes: 13 additions & 0 deletions COPYING
@@ -0,0 +1,13 @@
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.
Empty file added README.markdown
Empty file.
71 changes: 71 additions & 0 deletions Rakefile
@@ -0,0 +1,71 @@
require 'date'
require 'rake/clean'
require 'rake/extensiontask'
require 'digest/md5'

task :default => :test

# ==========================================================
# Ruby Extension
# ==========================================================

Rake::ExtensionTask.new('rinku')

# ==========================================================
# Testing
# ==========================================================

require 'rake/testtask'
Rake::TestTask.new('test') do |t|
t.test_files = FileList['test/*_test.rb']
t.ruby_opts += ['-rubygems'] if defined? Gem
end
task 'test' => [:compile]

# PACKAGING =================================================================

require 'rubygems'
$spec = eval(File.read('rinku.gemspec'))

def package(ext='')
"pkg/rinku-#{$spec.version}" + ext
end

desc 'Build packages'
task :package => package('.gem')

desc 'Build and install as local gem'
task :install => package('.gem') do
sh "gem install #{package('.gem')}"
end

desc 'Update the gemspec'
task :update_gem => file('rinku.gemspec')

directory 'pkg/'

file package('.gem') => %w[pkg/ rinku.gemspec] + $spec.files do |f|
sh "gem build rinku.gemspec"
mv File.basename(f.name), f.name
end

# GEMSPEC HELPERS ==========================================================

desc 'Gather required Upskirt sources into extension directory'
task :gather => 'upskirt/src/markdown.h' do |t|
files =
FileList[
'upskirt/src/{buffer,autolink}.h',
'upskirt/src/{buffer,autolink}.c',
'upskirt/html/html_autolink.c'
]
cp files, 'ext/rinku/',
:preserve => true,
:verbose => true
end

file 'upskirt/src/markdown.h' do |t|
abort "The Upskirt submodule is required."
end


1 change: 1 addition & 0 deletions VERSION
@@ -0,0 +1 @@
0.1.0
239 changes: 239 additions & 0 deletions ext/rinku/autolink.c
@@ -0,0 +1,239 @@
/*
* 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.
*/

#include "buffer.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

int
is_safe_link(const char *link, size_t link_len)
{
static const size_t valid_uris_count = 4;
static const char *valid_uris[] = {
"http://", "https://", "ftp://", "mailto://"
};

size_t i;

for (i = 0; i < valid_uris_count; ++i) {
size_t len = strlen(valid_uris[i]);

if (link_len > len &&
strncasecmp(link, valid_uris[i], len) == 0 &&
isalnum(link[len]))
return 1;
}

return 0;
}

static size_t
autolink_delim(char *data, size_t link_end, size_t offset, size_t size)
{
char cclose, copen = 0;

while (link_end > 0) {
if (strchr("?!.,", data[link_end - 1]) != NULL)
link_end--;

else if (data[link_end - 1] == ';') {
size_t new_end = link_end - 2;

while (new_end > 0 && isalpha(data[new_end]))
new_end--;

if (new_end < link_end - 2 && data[new_end] == '&')
link_end = new_end;
else
link_end--;
}

else if (data[link_end - 1] == '>') {
while (link_end > 0 && data[link_end] != '<')
link_end--;
}
else break;
}

if (link_end == 0)
return 0;

cclose = data[link_end - 1];

switch (cclose) {
case '"': copen = '"'; break;
case '\'': copen = '\''; break;
case ')': copen = '('; break;
case ']': copen = '['; break;
case '}': copen = '{'; break;
}

if (copen != 0) {
size_t closing = 0;
size_t opening = 0;
size_t i = 0;

/* Try to close the final punctuation sign in this same line;
* if we managed to close it outside of the URL, that means that it's
* not part of the URL. If it closes inside the URL, that means it
* is part of the URL.
*
* Examples:
*
* foo http://www.pokemon.com/Pikachu_(Electric) bar
* => http://www.pokemon.com/Pikachu_(Electric)
*
* foo (http://www.pokemon.com/Pikachu_(Electric)) bar
* => http://www.pokemon.com/Pikachu_(Electric)
*
* foo http://www.pokemon.com/Pikachu_(Electric)) bar
* => http://www.pokemon.com/Pikachu_(Electric))
*
* (foo http://www.pokemon.com/Pikachu_(Electric)) bar
* => foo http://www.pokemon.com/Pikachu_(Electric)
*/

while (i < link_end) {
if (data[i] == copen)
opening++;
else if (data[i] == cclose)
closing++;

i++;
}

if (closing != opening)
link_end--;
}

return link_end;
}

size_t
ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
{
size_t link_end;
int np = 0;

if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1]))
return 0;

if (size < 4 || memcmp(data, "www.", STRLEN("www.")) != 0)
return 0;

link_end = 0;
while (link_end < size && !isspace(data[link_end])) {
if (data[link_end] == '.')
np++;

link_end++;
}

if (np < 2)
return 0;

link_end = autolink_delim(data, link_end, offset, size);

if (link_end == 0)
return 0;

bufput(link, data, link_end);
*rewind_p = 0;

return (int)link_end;
}

size_t
ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
{
size_t link_end, rewind;
int nb = 0, np = 0;

for (rewind = 0; rewind < offset; ++rewind) {
char c = data[-rewind - 1];

if (isalnum(c))
continue;

if (strchr(".+-_", c) != NULL)
continue;

break;
}

if (rewind == 0)
return 0;

for (link_end = 0; link_end < size; ++link_end) {
char c = data[link_end];

if (isalnum(c))
continue;

if (c == '@')
nb++;
else if (c == '.' && link_end < size - 1)
np++;
else if (c != '-' && c != '_')
break;
}

if (link_end < 2 || nb != 1 || np == 0)
return 0;

link_end = autolink_delim(data, link_end, offset, size);

if (link_end == 0)
return 0;

bufput(link, data - rewind, link_end + rewind);
*rewind_p = rewind;

return link_end;
}

size_t
ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
{
size_t link_end, rewind = 0;

if (size < 4 || data[1] != '/' || data[2] != '/')
return 0;

while (rewind < offset && isalpha(data[-rewind - 1]))
rewind++;

if (!is_safe_link(data - rewind, size + rewind))
return 0;

link_end = 0;
while (link_end < size && !isspace(data[link_end]))
link_end++;

link_end = autolink_delim(data, link_end, offset, size);

if (link_end == 0)
return 0;

bufput(link, data - rewind, link_end + rewind);
*rewind_p = rewind;

return link_end;
}

39 changes: 39 additions & 0 deletions ext/rinku/autolink.h
@@ -0,0 +1,39 @@
/*
* 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.
*/

#ifndef UPSKIRT_AUTOLINK_H
#define UPSKIRT_AUTOLINK_H_H

#include "buffer.h"

typedef enum {
AUTOLINK_URLS = (1 << 0),
AUTOLINK_EMAILS = (1 << 1),
AUTOLINK_ALL = AUTOLINK_URLS|AUTOLINK_EMAILS
} autolink_mode;

extern size_t
ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);

extern size_t
ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);

extern size_t
ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);

#endif

/* vim: set filetype=c: */

0 comments on commit 96e7db3

Please sign in to comment.