mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-23 05:48:11 -05:00
New features:
* Discount 1.6.1
* Lines beginning with spaces are considered code. I don't know why markdown.py didn't trigger this.
* tables in mark down: why not?
* validation of resulting HTML vial libxml to prevent hax.
* private RSS and JSON feeds
* optional whitelists for subreddits
* Moderator messaging
Additions:
* destination sanitization to cut down on XSRF
* cosmetic fix to spam and reported listing
* make the rss feeds on messages useful
* /admin/errors
* Of the types of listings hitting the precomputers (top/controversy by hour/day/week/month/year), the ones over long periods of time don't change often. So we can try to run them at most once per day, and then merge in the day's listings.
* google analytics
* logging queue
* Created empty commentspanel.xml, errorpage.xml, login.xml
* add subreddit rules/info box to submit page
* add 'via' link on messages in moderator inbox
* add a show=all get parameter to link listings to optionally ignore hiding preferences.
* Raise edited timelimit to three mins
* Remove UI that makes it look like you can edit deleted selftexts
* Make it clearer to admins when a link is deleted
* Fix [S] leak on deleted comments
* Fix /user/[deleted] misrendering
* New house ads system
* updated so that minimalcontrollers actually can use the page cache.
* Added /admin/usage
Bugfixes:
* Reduce the number of results that we request from Solr and simplify that caching a bit
* Require a secret key to shut down app-servers
* Make get_title a little more resilient to malformed documents and slow remote servers
* Cause the SearchBuilder to only byID the results that it's going to render instead of all 1000
* Remove ability for an author to XSS himself
* fix spam listings and an xsrf
* More verbose VDestination
* Fixing the famous ?limit=0.1 error, and one last password-validation one
* distinguish deleted comments' and deleted links' error messages
* Don't allow ridiculously long log lines to widen the page
* Bug with HardCache.add() when existing key is expired
* Add adminbox next to domain
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -30,3 +30,4 @@ r2/srcount.pickle
|
||||
r2/myproduction.ini
|
||||
.DS_Store
|
||||
r2/r2.egg-info/**
|
||||
r2/r2/public/static/sprite.png
|
||||
|
||||
@@ -9,7 +9,7 @@ template_debug = true
|
||||
uncompressedJS = true
|
||||
translator = true
|
||||
sqlprinting = false
|
||||
|
||||
exception_logging = false
|
||||
log_start = true
|
||||
|
||||
proxy_addr =
|
||||
@@ -30,6 +30,9 @@ adframetracker_url =
|
||||
clicktracker_url =
|
||||
traffic_url =
|
||||
|
||||
# Just a list of words. Used by errlog.py to make up names for new errors.
|
||||
words_file = /usr/dict/words
|
||||
|
||||
# for sponsored links:
|
||||
payment_domain = https://pay.localhost/
|
||||
authorizenetname =
|
||||
@@ -93,6 +96,9 @@ db_table_report_account_subreddit = relation, account, subreddit, main
|
||||
db_table_award = thing, award
|
||||
db_table_trophy = relation, account, award, award
|
||||
|
||||
db_table_ad = thing, main
|
||||
db_table_adsr = relation, ad, subreddit, main
|
||||
|
||||
disallow_db_writes = False
|
||||
|
||||
###
|
||||
@@ -103,6 +109,8 @@ timezone = UTC
|
||||
lang = en
|
||||
monitored_servers = localhost
|
||||
|
||||
enable_usage_stats = false
|
||||
|
||||
#query cache settings
|
||||
num_query_queue_workers = 0
|
||||
query_queue_worker =
|
||||
@@ -174,6 +182,10 @@ sr_dropdown_threshold = 15
|
||||
|
||||
smtp_server = localhost
|
||||
new_link_share_delay = 5 minutes
|
||||
|
||||
# email address of the person / people running your site
|
||||
nerds_email = root@localhost
|
||||
|
||||
share_reply = noreply@yourdomain.com
|
||||
|
||||
#user-agents to limit
|
||||
|
||||
@@ -33,7 +33,7 @@ from pylons.wsgiapp import PylonsApp, PylonsBaseWSGIApp
|
||||
|
||||
from r2.config.environment import load_environment
|
||||
from r2.config.rewrites import rewrites
|
||||
from r2.lib.utils import rstrips
|
||||
from r2.lib.utils import rstrips, is_authorized_cname
|
||||
from r2.lib.jsontemplates import api_type
|
||||
|
||||
#middleware stuff
|
||||
@@ -245,11 +245,10 @@ class DomainMiddleware(object):
|
||||
auth_cnames = [x.strip() for x in auth_cnames.split(',')]
|
||||
# we are going to be matching with endswith, so make sure there
|
||||
# are no empty strings that have snuck in
|
||||
self.auth_cnames = [x for x in auth_cnames if x]
|
||||
self.auth_cnames = filter(None, auth_cnames)
|
||||
|
||||
def is_auth_cname(self, domain):
|
||||
return any((domain == cname or domain.endswith('.' + cname))
|
||||
for cname in self.auth_cnames)
|
||||
return is_authorized_cname(domain, self.auth_cnames)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# get base domain as defined in INI file
|
||||
|
||||
@@ -31,7 +31,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc = map.connect
|
||||
|
||||
admin_routes.add(mc)
|
||||
|
||||
|
||||
mc('/login', controller='front', action='login')
|
||||
mc('/logout', controller='front', action='logout')
|
||||
mc('/verify', controller='front', action='verify')
|
||||
@@ -41,15 +41,16 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/validuser', controller='front', action='validuser')
|
||||
|
||||
mc('/over18', controller='post', action='over18')
|
||||
|
||||
|
||||
mc('/search', controller='front', action='search')
|
||||
|
||||
mc('/sup', controller='front', action='sup')
|
||||
mc('/traffic', controller='front', action='site_traffic')
|
||||
|
||||
|
||||
mc('/about/message/:where', controller='message', action='listing')
|
||||
mc('/about/:location', controller='front',
|
||||
action='editreddit', location = 'about')
|
||||
|
||||
|
||||
mc('/reddits/create', controller='front', action='newreddit')
|
||||
mc('/reddits/search', controller='front', action='search_reddits')
|
||||
mc('/reddits/login', controller='front', action='login')
|
||||
@@ -60,42 +61,51 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/reddits/mine/:where', controller='myreddits', action='listing',
|
||||
where='subscriber',
|
||||
requirements=dict(where='subscriber|contributor|moderator'))
|
||||
|
||||
|
||||
mc('/buttons', controller='buttons', action='button_demo_page')
|
||||
#the frame
|
||||
mc('/button_content', controller='buttons', action='button_content')
|
||||
#/button.js and buttonlite.js - the embeds
|
||||
mc('/button', controller='buttons', action='button_embed')
|
||||
mc('/button', controller='buttonjs', action='button_embed')
|
||||
mc('/buttonlite', controller='buttons', action='button_lite')
|
||||
|
||||
|
||||
mc('/widget', controller='buttons', action='widget_demo_page')
|
||||
mc('/bookmarklets', controller='buttons', action='bookmarklets')
|
||||
|
||||
|
||||
mc('/awards', controller='front', action='awards')
|
||||
|
||||
|
||||
mc('/i18n', controller='feedback', action='i18n')
|
||||
mc('/feedback', controller='feedback', action='feedback')
|
||||
mc('/ad_inq', controller='feedback', action='ad_inq')
|
||||
|
||||
|
||||
|
||||
mc('/admin/i18n', controller='i18n', action='list')
|
||||
mc('/admin/i18n/:action', controller='i18n')
|
||||
mc('/admin/i18n/:action/:lang', controller='i18n')
|
||||
|
||||
mc('/admin/usage', controller='usage')
|
||||
|
||||
# Used for editing ads
|
||||
mc('/admin/ads', controller='ads')
|
||||
mc('/admin/ads/:adcn/:action', controller='ads',
|
||||
requirements=dict(action="assign|srs"))
|
||||
|
||||
mc('/admin/awards', controller='awards')
|
||||
mc('/admin/awards/:awardcn/:action', controller='awards',
|
||||
requirements=dict(action="give|winners"))
|
||||
|
||||
mc('/admin/errors', controller='errorlog')
|
||||
|
||||
mc('/admin/:action', controller='admin')
|
||||
|
||||
|
||||
mc('/user/:username/about', controller='user', action='about',
|
||||
where='overview')
|
||||
mc('/user/:username/:where', controller='user', action='listing',
|
||||
where='overview')
|
||||
|
||||
|
||||
mc('/prefs/:location', controller='front',
|
||||
action='prefs', location='options')
|
||||
|
||||
|
||||
mc('/info/0:article/*rest', controller = 'front',
|
||||
action='oldinfo', dest='comments', type='ancient')
|
||||
mc('/info/:article/:dest/:comment', controller='front',
|
||||
@@ -113,7 +123,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
action = 'comments', title=None, comment = None)
|
||||
mc('/duplicates/:article/:title', controller = 'front',
|
||||
action = 'duplicates', title=None)
|
||||
|
||||
|
||||
mc('/mail/optout', controller='front', action = 'optout')
|
||||
mc('/mail/optin', controller='front', action = 'optin')
|
||||
mc('/stylesheet', controller = 'front', action = 'stylesheet')
|
||||
@@ -138,8 +148,8 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/shutdown', controller='health', action='shutdown')
|
||||
|
||||
mc('/', controller='hot', action='listing')
|
||||
|
||||
listing_controllers = "hot|saved|toplinks|new|recommended|randomrising|comments"
|
||||
|
||||
listing_controllers = "hot|saved|new|recommended|randomrising|comments"
|
||||
|
||||
mc('/:controller', action='listing',
|
||||
requirements=dict(controller=listing_controllers))
|
||||
@@ -148,18 +158,20 @@ def make_map(global_conf={}, app_conf={}):
|
||||
|
||||
mc('/:sort', controller='browse', sort='top', action = 'listing',
|
||||
requirements = dict(sort = 'top|controversial'))
|
||||
|
||||
|
||||
mc('/message/compose', controller='message', action='compose')
|
||||
mc('/message/messages/:mid', controller='message', action='listing',
|
||||
where = "messages")
|
||||
mc('/message/:where', controller='message', action='listing')
|
||||
|
||||
mc('/message/moderator/:subwhere', controller='message', action='listing',
|
||||
where = 'moderator')
|
||||
|
||||
mc('/:action', controller='front',
|
||||
requirements=dict(action="password|random|framebuster"))
|
||||
mc('/:action', controller='embed',
|
||||
requirements=dict(action="help|blog"))
|
||||
mc('/help/*anything', controller='embed', action='help')
|
||||
|
||||
|
||||
mc('/goto', controller='toolbar', action='goto')
|
||||
mc('/tb/:id', controller='toolbar', action='tb')
|
||||
mc('/toolbar/:action', controller='toolbar',
|
||||
@@ -172,7 +184,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
# additional toolbar-related rules just above the catchall
|
||||
|
||||
mc('/d/:what', controller='api', action='bookmarklet')
|
||||
|
||||
|
||||
mc('/resetpassword/:key', controller='front',
|
||||
action='resetpassword')
|
||||
mc('/verification/:key', controller='front',
|
||||
@@ -184,7 +196,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
requirements=dict(action="login|reg"))
|
||||
mc('/post/:action', controller='post',
|
||||
requirements=dict(action="options|over18|unlogged_options|optout|optin|login|reg"))
|
||||
|
||||
|
||||
mc('/api/distinguish/:how', controller='api', action="distinguish")
|
||||
mc('/api/:action/:url_user', controller='api',
|
||||
requirements=dict(action="login|register"))
|
||||
@@ -193,7 +205,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/api/:action', controller='promote',
|
||||
requirements=dict(action="promote|unpromote|new_promo|link_thumb|freebie|promote_note|update_pay|refund|traffic_viewer|rm_traffic_viewer"))
|
||||
mc('/api/:action', controller='api')
|
||||
|
||||
|
||||
mc('/captcha/:iden', controller='captcha', action='captchaimg')
|
||||
|
||||
mc('/mediaembed/:link', controller="mediaembed", action="mediaembed")
|
||||
@@ -202,17 +214,23 @@ def make_map(global_conf={}, app_conf={}):
|
||||
|
||||
mc('/store', controller='redirect', action='redirect',
|
||||
dest='http://store.reddit.com/index.html')
|
||||
|
||||
|
||||
mc('/code', controller='redirect', action='redirect',
|
||||
dest='http://code.reddit.com/')
|
||||
|
||||
|
||||
mc('/mobile', controller='redirect', action='redirect',
|
||||
dest='http://m.reddit.com/')
|
||||
|
||||
mc('/authorize_embed', controller = 'front', action = 'authorize_embed')
|
||||
|
||||
mc("/ads/", controller = "front", action = "ad")
|
||||
mc("/ads/:reddit", controller = "front", action = "ad")
|
||||
|
||||
# Used for showing ads
|
||||
mc("/ads/", controller = "mediaembed", action = "ad")
|
||||
mc("/ads/r/:reddit_name", controller = "mediaembed", action = "ad")
|
||||
mc("/ads/:codename", controller = "mediaembed", action = "ad_by_codename")
|
||||
|
||||
mc('/comscore-iframe/', controller='mediaembed', action='comscore')
|
||||
mc('/comscore-iframe/*url', controller='mediaembed', action='comscore')
|
||||
|
||||
# This route handles displaying the error page and
|
||||
# graphics used in the 404/500
|
||||
# error pages. It should likely stay at the top
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
from listingcontroller import ListingController
|
||||
from listingcontroller import HotController
|
||||
from listingcontroller import SavedController
|
||||
from listingcontroller import ToplinksController
|
||||
from listingcontroller import NewController
|
||||
from listingcontroller import BrowseController
|
||||
from listingcontroller import RecommendedController
|
||||
@@ -39,6 +38,7 @@ from feedback import FeedbackController
|
||||
from front import FrontController
|
||||
from health import HealthController
|
||||
from buttons import ButtonsController
|
||||
from buttons import ButtonjsController
|
||||
from captcha import CaptchaController
|
||||
from embed import EmbedController
|
||||
from error import ErrorController
|
||||
@@ -46,6 +46,9 @@ from post import PostController
|
||||
from toolbar import ToolbarController
|
||||
from i18n import I18nController
|
||||
from awards import AwardsController
|
||||
from ads import AdsController
|
||||
from usage import UsageController
|
||||
from errorlog import ErrorlogController
|
||||
from promotecontroller import PromoteController
|
||||
from mediaembed import MediaembedController
|
||||
|
||||
|
||||
56
r2/r2/controllers/ads.py
Normal file
56
r2/r2/controllers/ads.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of the
|
||||
# Original Code is CondeNet, Inc.
|
||||
#
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from pylons import request, g
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.pages import AdminPage, AdminAds, AdminAdAssign, AdminAdSRs
|
||||
from validator import *
|
||||
|
||||
class AdsController(RedditController):
|
||||
|
||||
@validate(VSponsor())
|
||||
def GET_index(self):
|
||||
res = AdminPage(content = AdminAds(),
|
||||
show_sidebar = False,
|
||||
title = 'ads').render()
|
||||
return res
|
||||
|
||||
@validate(VSponsor(),
|
||||
ad = VAdByCodename('adcn'))
|
||||
def GET_assign(self, ad):
|
||||
if ad is None:
|
||||
abort(404, 'page not found')
|
||||
|
||||
res = AdminPage(content = AdminAdAssign(ad),
|
||||
show_sidebar = False,
|
||||
title='assign an ad to a community').render()
|
||||
return res
|
||||
|
||||
@validate(VSponsor(),
|
||||
ad = VAdByCodename('adcn'))
|
||||
def GET_srs(self, ad):
|
||||
if ad is None:
|
||||
abort(404, 'page not found')
|
||||
|
||||
res = AdminPage(content = AdminAdSRs(ad),
|
||||
show_sidebar = False,
|
||||
title='ad srs').render()
|
||||
return res
|
||||
@@ -50,10 +50,25 @@ from r2.lib.media import force_thumbnail, thumbnail_url
|
||||
from r2.lib.comment_tree import add_comment, delete_comment
|
||||
from r2.lib import tracking, cssfilter, emailer
|
||||
from r2.lib.subreddit_search import search_reddits
|
||||
from r2.lib.log import log_text
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from md5 import md5
|
||||
|
||||
def reject_vote(thing):
|
||||
voteword = request.params.get('dir')
|
||||
|
||||
if voteword == '1':
|
||||
voteword = 'upvote'
|
||||
elif voteword == '0':
|
||||
voteword = '0-vote'
|
||||
elif voteword == '-1':
|
||||
voteword = 'downvote'
|
||||
|
||||
log_text ("rejected vote", "Rejected %s from %s (%s) on %s %s via %s" %
|
||||
(voteword, c.user.name, request.ip, thing.__class__.__name__,
|
||||
thing._id36, request.referer), "info")
|
||||
|
||||
class ApiController(RedditController):
|
||||
"""
|
||||
Controller which deals with almost all AJAX site interaction.
|
||||
@@ -73,6 +88,9 @@ class ApiController(RedditController):
|
||||
return abort(404, 'not found')
|
||||
|
||||
links = link_from_url(request.params.get('url'), filter_spam = False)
|
||||
if not links:
|
||||
return abort(404, 'not found')
|
||||
|
||||
listing = wrap_links(links, num = count)
|
||||
return BoringPage(_("API"), content = listing).render()
|
||||
|
||||
@@ -107,19 +125,19 @@ class ApiController(RedditController):
|
||||
VUser(),
|
||||
VModhash(),
|
||||
ip = ValidIP(),
|
||||
to = VExistingUname('to'),
|
||||
to = VMessageRecipent('to'),
|
||||
subject = VRequired('subject', errors.NO_SUBJECT),
|
||||
body = VMessage(['text', 'message']))
|
||||
body = VMarkdown(['text', 'message']))
|
||||
def POST_compose(self, form, jquery, to, subject, body, ip):
|
||||
"""
|
||||
handles message composition under /message/compose.
|
||||
"""
|
||||
if not (form.has_errors("to", errors.USER_DOESNT_EXIST,
|
||||
errors.NO_USER) or
|
||||
errors.NO_USER, errors.SUBREDDIT_NOEXIST) or
|
||||
form.has_errors("subject", errors.NO_SUBJECT) or
|
||||
form.has_errors("text", errors.NO_TEXT, errors.TOO_LONG) or
|
||||
form.has_errors("captcha", errors.BAD_CAPTCHA)):
|
||||
|
||||
|
||||
m, inbox_rel = Message._new(c.user, to, subject, body, ip)
|
||||
form.set_html(".status", _("your message has been delivered"))
|
||||
form.set_inputs(to = "", subject = "", text = "", captcha="")
|
||||
@@ -128,20 +146,20 @@ class ApiController(RedditController):
|
||||
|
||||
@validatedForm(VUser(),
|
||||
VCaptcha(),
|
||||
ValidDomain('url'),
|
||||
VRatelimit(rate_user = True, rate_ip = True,
|
||||
prefix = "rate_submit_"),
|
||||
ip = ValidIP(),
|
||||
sr = VSubmitSR('sr'),
|
||||
url = VUrl(['url', 'sr']),
|
||||
banmsg = VOkayDomain('url'),
|
||||
title = VTitle('title'),
|
||||
save = VBoolean('save'),
|
||||
selftext = VSelfText('text'),
|
||||
selftext = VMarkdown('text'),
|
||||
kind = VOneOf('kind', ['link', 'self', 'poll']),
|
||||
then = VOneOf('then', ('tb', 'comments'),
|
||||
default='comments'))
|
||||
def POST_submit(self, form, jquery, url, selftext, kind, title, save,
|
||||
sr, ip, then):
|
||||
def POST_submit(self, form, jquery, url, banmsg, selftext, kind, title,
|
||||
save, sr, ip, then):
|
||||
#backwards compatability
|
||||
if url == 'self':
|
||||
kind = 'self'
|
||||
@@ -178,6 +196,11 @@ class ApiController(RedditController):
|
||||
elif form.has_errors("title", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
# Uncomment if we want to let spammers know we're on to them
|
||||
# if banmsg:
|
||||
# form.set_html(".field-url.BAD_URL", banmsg)
|
||||
# return
|
||||
|
||||
elif kind == 'self' and form.has_errors('text', errors.TOO_LONG):
|
||||
pass
|
||||
|
||||
@@ -194,6 +217,9 @@ class ApiController(RedditController):
|
||||
l = Link._submit(request.post.title, url if kind == 'link' else 'self',
|
||||
c.user, sr, ip)
|
||||
|
||||
if banmsg:
|
||||
admintools.spam(l, banner = "domain (%s)" % banmsg)
|
||||
|
||||
if kind == 'self':
|
||||
l.url = l.make_permalink_slow()
|
||||
l.is_self = True
|
||||
@@ -264,31 +290,18 @@ class ApiController(RedditController):
|
||||
rem = VBoolean('rem'),
|
||||
reason = VReason('reason'))
|
||||
def POST_login(self, form, jquery, user, username, dest, rem, reason):
|
||||
|
||||
if reason and reason[0] == 'redirect':
|
||||
dest = reason[1]
|
||||
|
||||
hc_key = "login_attempts-%s" % request.ip
|
||||
|
||||
# TODO: You-know-what (not mentioning it, just in case
|
||||
# we accidentally release code with this comment in it)
|
||||
|
||||
# Cache lifetime for login_attmempts
|
||||
la_expire_time = 3600 * 8
|
||||
|
||||
recent_attempts = g.hardcache.add(hc_key, 0, time=la_expire_time)
|
||||
|
||||
fake_failure = False
|
||||
if recent_attempts >= 25:
|
||||
g.log.error ("%s failed to login as %s (attempt #%d)"
|
||||
% (request.ip, username, recent_attempts))
|
||||
fake_failure = True
|
||||
|
||||
if fake_failure or form.has_errors("passwd", errors.WRONG_PASSWORD):
|
||||
if login_throttle(username, wrong_password = form.has_errors("passwd",
|
||||
errors.WRONG_PASSWORD)):
|
||||
VRatelimit.ratelimit(rate_ip = True, prefix = 'login_', seconds=1)
|
||||
g.hardcache.incr(hc_key, time = la_expire_time)
|
||||
else:
|
||||
self._login(form, user, dest, rem)
|
||||
|
||||
c.errors.add(errors.WRONG_PASSWORD, field = "passwd")
|
||||
|
||||
if not form.has_errors("passwd", errors.WRONG_PASSWORD):
|
||||
self._login(form, user, dest, rem)
|
||||
|
||||
@validatedForm(VCaptcha(),
|
||||
VRatelimit(rate_ip = True, prefix = "rate_register_"),
|
||||
@@ -310,11 +323,11 @@ class ApiController(RedditController):
|
||||
|
||||
user = register(name, password)
|
||||
VRatelimit.ratelimit(rate_ip = True, prefix = "rate_register_")
|
||||
|
||||
|
||||
#anything else we know (email, languages)?
|
||||
if email:
|
||||
user.email = email
|
||||
|
||||
|
||||
user.pref_lang = c.lang
|
||||
if c.content_langs == 'all':
|
||||
user.pref_content_langs = 'all'
|
||||
@@ -322,10 +335,10 @@ class ApiController(RedditController):
|
||||
langs = list(c.content_langs)
|
||||
langs.sort()
|
||||
user.pref_content_langs = tuple(langs)
|
||||
|
||||
|
||||
d = c.user._dirties.copy()
|
||||
user._commit()
|
||||
|
||||
|
||||
c.user = user
|
||||
if reason:
|
||||
if reason[0] == 'redirect':
|
||||
@@ -333,7 +346,7 @@ class ApiController(RedditController):
|
||||
elif reason[0] == 'subscribe':
|
||||
for sr, sub in reason[1].iteritems():
|
||||
self._subscribe(sr, sub)
|
||||
|
||||
|
||||
self._login(form, user, dest, rem)
|
||||
|
||||
@noresponse(VUser(),
|
||||
@@ -530,7 +543,7 @@ class ApiController(RedditController):
|
||||
if isinstance(thing, Link):
|
||||
sr = thing.subreddit_slow
|
||||
expire_hot(sr)
|
||||
queries.new_link(thing)
|
||||
queries.delete_links(thing)
|
||||
|
||||
#comments have special delete tasks
|
||||
elif isinstance(thing, Comment):
|
||||
@@ -567,11 +580,11 @@ class ApiController(RedditController):
|
||||
@validatedForm(VUser(),
|
||||
VModhash(),
|
||||
item = VByNameIfAuthor('thing_id'),
|
||||
text = VComment('text'))
|
||||
text = VMarkdown('text'))
|
||||
def POST_editusertext(self, form, jquery, item, text):
|
||||
if not form.has_errors("text",
|
||||
errors.NO_TEXT, errors.TOO_LONG,
|
||||
errors.NOT_AUTHOR):
|
||||
if (not form.has_errors("text",
|
||||
errors.NO_TEXT, errors.TOO_LONG) and
|
||||
not form.has_errors("thing_id", errors.NOT_AUTHOR)):
|
||||
|
||||
if isinstance(item, Comment):
|
||||
kind = 'comment'
|
||||
@@ -580,7 +593,10 @@ class ApiController(RedditController):
|
||||
kind = 'link'
|
||||
item.selftext = text
|
||||
|
||||
if (item._date < timeago('60 seconds')
|
||||
if item._deleted:
|
||||
return abort(403, "forbidden")
|
||||
|
||||
if (item._date < timeago('3 minutes')
|
||||
or (item._ups + item._downs > 2)):
|
||||
item.editted = True
|
||||
|
||||
@@ -601,7 +617,7 @@ class ApiController(RedditController):
|
||||
prefix = "rate_comment_"),
|
||||
ip = ValidIP(),
|
||||
parent = VSubmitParent(['thing_id', 'parent']),
|
||||
comment = VComment(['text', 'comment']))
|
||||
comment = VMarkdown(['text', 'comment']))
|
||||
def POST_comment(self, commentform, jquery, parent, comment, ip):
|
||||
should_ratelimit = True
|
||||
#check the parent type here cause we need that for the
|
||||
@@ -633,7 +649,8 @@ class ApiController(RedditController):
|
||||
not commentform.has_errors("ratelimit",
|
||||
errors.RATELIMIT) and
|
||||
not commentform.has_errors("parent",
|
||||
errors.DELETED_COMMENT)):
|
||||
errors.DELETED_COMMENT,
|
||||
errors.DELETED_LINK)):
|
||||
|
||||
if is_message:
|
||||
to = Account._byID(parent.author_id)
|
||||
@@ -650,11 +667,9 @@ class ApiController(RedditController):
|
||||
queries.queue_vote(c.user, item, True, ip,
|
||||
cheater = (errors.CHEATER, None) in c.errors)
|
||||
|
||||
#update last modified
|
||||
set_last_modified(link, 'comments')
|
||||
|
||||
#update the comment cache
|
||||
add_comment(item)
|
||||
# adding to comments-tree is done as part of
|
||||
# newcomments_q, so if they refresh immediately they
|
||||
# won't see their comment
|
||||
|
||||
# clean up the submission form and remove it from the DOM (if reply)
|
||||
t = commentform.find("textarea")
|
||||
@@ -666,7 +681,7 @@ class ApiController(RedditController):
|
||||
|
||||
# insert the new comment
|
||||
jquery.insert_things(item)
|
||||
|
||||
|
||||
# remove any null listings that may be present
|
||||
jquery("#noresults").hide()
|
||||
|
||||
@@ -751,14 +766,12 @@ class ApiController(RedditController):
|
||||
return
|
||||
|
||||
if vote_type == "rejected":
|
||||
g.log.error("POST_vote: rejected vote (%s) from '%s' on %s (%s)"%
|
||||
(request.params.get('dir'), c.user.name,
|
||||
thing._fullname, request.ip))
|
||||
reject_vote(thing)
|
||||
store = False
|
||||
|
||||
# TODO: temporary hack until we migrate the rest of the vote data
|
||||
if thing._date < datetime(2009, 4, 17, 0, 0, 0, 0, g.tz):
|
||||
g.log.error("POST_vote: ignoring old vote on %s" % thing._fullname)
|
||||
g.log.debug("POST_vote: ignoring old vote on %s" % thing._fullname)
|
||||
store = False
|
||||
|
||||
# in a lock to prevent duplicate votes from people
|
||||
@@ -982,21 +995,20 @@ class ApiController(RedditController):
|
||||
name = VSubredditName("name"),
|
||||
title = VLength("title", max_length = 100),
|
||||
domain = VCnameDomain("domain"),
|
||||
description = VLength("description", max_length = 1000),
|
||||
description = VMarkdown("description", max_length = 1000),
|
||||
lang = VLang("lang"),
|
||||
over_18 = VBoolean('over_18'),
|
||||
allow_top = VBoolean('allow_top'),
|
||||
show_media = VBoolean('show_media'),
|
||||
use_whitelist = VBoolean('use_whitelist'),
|
||||
type = VOneOf('type', ('public', 'private', 'restricted')),
|
||||
ip = ValidIP(),
|
||||
ad_type = VOneOf('ad', ('default', 'basic', 'custom')),
|
||||
ad_file = VLength('ad-location', max_length = 500),
|
||||
sponsor_text =VLength('sponsorship-text', max_length = 500),
|
||||
sponsor_name =VLength('sponsorship-name', max_length = 500),
|
||||
sponsor_url = VLength('sponsorship-url', max_length = 500),
|
||||
css_on_cname = VBoolean("css_on_cname"),
|
||||
)
|
||||
def POST_site_admin(self, form, jquery, name, ip, sr, ad_type, ad_file,
|
||||
def POST_site_admin(self, form, jquery, name, ip, sr,
|
||||
sponsor_text, sponsor_url, sponsor_name, **kw):
|
||||
# the status button is outside the form -- have to reset by hand
|
||||
form.parent().set_html('.status', "")
|
||||
@@ -1005,7 +1017,7 @@ class ApiController(RedditController):
|
||||
kw = dict((k, v) for k, v in kw.iteritems()
|
||||
if k in ('name', 'title', 'domain', 'description', 'over_18',
|
||||
'show_media', 'type', 'lang', "css_on_cname",
|
||||
'allow_top'))
|
||||
'allow_top', 'use_whitelist'))
|
||||
|
||||
#if a user is banned, return rate-limit errors
|
||||
if c.user._spam:
|
||||
@@ -1054,10 +1066,6 @@ class ApiController(RedditController):
|
||||
elif sr.is_moderator(c.user) or c.user_is_admin:
|
||||
|
||||
if c.user_is_admin:
|
||||
sr.ad_type = ad_type
|
||||
if ad_type != "custom":
|
||||
ad_file = Subreddit._defaults['ad_file']
|
||||
sr.ad_file = ad_file
|
||||
sr.sponsorship_text = sponsor_text or ""
|
||||
sr.sponsorship_url = sponsor_url or None
|
||||
sr.sponsorship_name = sponsor_name or None
|
||||
@@ -1137,50 +1145,58 @@ class ApiController(RedditController):
|
||||
if r:
|
||||
queries.new_savehide(r)
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
thing = VByName('id', multiple = True))
|
||||
def POST_collapse_message(self, thing):
|
||||
if not thing:
|
||||
def collapse_handler(self, things, collapse):
|
||||
if not things:
|
||||
return
|
||||
for t in tup(thing):
|
||||
things = tup(things)
|
||||
srs = Subreddit._byID([t.sr_id for t in things if t.sr_id],
|
||||
return_dict = True)
|
||||
for t in things:
|
||||
if hasattr(t, "to_id") and c.user._id == t.to_id:
|
||||
t.to_collapse = True
|
||||
t.to_collapse = collapse
|
||||
elif hasattr(t, "author_id") and c.user._id == t.author_id:
|
||||
t.author_collapse = True
|
||||
t.author_collapse = collapse
|
||||
elif isinstance(t, Message) and t.sr_id:
|
||||
if srs[t.sr_id].is_moderator(c.user):
|
||||
t.to_collapse = collapse
|
||||
t._commit()
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
thing = VByName('id', multiple = True))
|
||||
def POST_uncollapse_message(self, thing):
|
||||
things = VByName('id', multiple = True))
|
||||
def POST_collapse_message(self, things):
|
||||
self.collapse_handler(things, True)
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
things = VByName('id', multiple = True))
|
||||
def POST_uncollapse_message(self, things):
|
||||
self.collapse_handler(things, False)
|
||||
|
||||
def unread_handler(self, thing, unread):
|
||||
if not thing:
|
||||
return
|
||||
for t in tup(thing):
|
||||
if hasattr(t, "to_id") and c.user._id == t.to_id:
|
||||
t.to_collapse = False
|
||||
elif hasattr(t, "author_id") and c.user._id == t.author_id:
|
||||
t.author_collapse = False
|
||||
t._commit()
|
||||
# if the message has a recipient, try validating that
|
||||
# desitination first (as it is cheaper and more common)
|
||||
if not hasattr(thing, "to_id") or c.user._id == thing.to_id:
|
||||
queries.set_unread(thing, c.user, unread)
|
||||
# if the message is for a subreddit, check that next
|
||||
if hasattr(thing, "sr_id"):
|
||||
sr = thing.subreddit_slow
|
||||
if sr and sr.is_moderator(c.user):
|
||||
queries.set_unread(thing, sr, unread)
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
thing = VByName('id'))
|
||||
def POST_unread_message(self, thing):
|
||||
if not thing:
|
||||
return
|
||||
if hasattr(thing, "to_id") and c.user._id != thing.to_id:
|
||||
return
|
||||
queries.set_unread(thing, True)
|
||||
self.unread_handler(thing, True)
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
thing = VByName('id'))
|
||||
def POST_read_message(self, thing):
|
||||
if not thing: return
|
||||
if hasattr(thing, "to_id") and c.user._id != thing.to_id:
|
||||
return
|
||||
queries.set_unread(thing, False)
|
||||
self.unread_handler(thing, False)
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
@@ -1203,10 +1219,14 @@ class ApiController(RedditController):
|
||||
@validatedForm(VUser(),
|
||||
parent = VByName('parent_id'))
|
||||
def POST_moremessages(self, form, jquery, parent):
|
||||
if not parent.can_view():
|
||||
if not parent.can_view_slow():
|
||||
return self.abort(403,'forbidden')
|
||||
|
||||
builder = MessageBuilder(c.user, parent = parent, skip = False)
|
||||
if parent.sr_id:
|
||||
builder = SrMessageBuilder(parent.subreddit_slow,
|
||||
parent = parent, skip = False)
|
||||
else:
|
||||
builder = UserMessageBuilder(c.user, parent = parent, skip = False)
|
||||
listing = Listing(builder).listing()
|
||||
a = []
|
||||
for item in listing.things:
|
||||
@@ -1221,19 +1241,18 @@ class ApiController(RedditController):
|
||||
@validatedForm(link = VByName('link_id'),
|
||||
sort = VMenu('where', CommentSortMenu),
|
||||
children = VCommentIDs('children'),
|
||||
depth = VInt('depth', min = 0, max = 8),
|
||||
mc_id = nop('id'))
|
||||
def POST_morechildren(self, form, jquery,
|
||||
link, sort, children, depth, mc_id):
|
||||
link, sort, children, mc_id):
|
||||
user = c.user if c.user_is_loggedin else None
|
||||
if not link or not link.subreddit_slow.can_view(user):
|
||||
return self.abort(403,'forbidden')
|
||||
return abort(403,'forbidden')
|
||||
|
||||
if children:
|
||||
builder = CommentBuilder(link, CommentSortMenu.operator(sort),
|
||||
children)
|
||||
listing = Listing(builder, nextprev = False)
|
||||
items = listing.get_items(starting_depth = depth, num = 20)
|
||||
items = listing.get_items(num = 20)
|
||||
def _children(cur_items):
|
||||
items = []
|
||||
for cm in cur_items:
|
||||
@@ -1399,6 +1418,107 @@ class ApiController(RedditController):
|
||||
tr._is_enabled = True
|
||||
|
||||
|
||||
@validatedForm(VAdmin(),
|
||||
hexkey=VLength("hexkey", max_length=32),
|
||||
nickname=VLength("nickname", max_length = 1000),
|
||||
status = VOneOf("status",
|
||||
("new", "severe", "interesting", "normal", "fixed")))
|
||||
def POST_edit_error(self, form, jquery, hexkey, nickname, status):
|
||||
if form.has_errors(("hexkey", "nickname", "status"),
|
||||
errors.NO_TEXT, errors.INVALID_OPTION):
|
||||
pass
|
||||
|
||||
if form.has_error():
|
||||
return
|
||||
|
||||
key = "error_nickname-%s" % str(hexkey)
|
||||
g.hardcache.set(key, nickname, 86400 * 365)
|
||||
|
||||
key = "error_status-%s" % str(hexkey)
|
||||
g.hardcache.set(key, status, 86400 * 365)
|
||||
|
||||
form.set_html(".status", _('saved'))
|
||||
|
||||
@validatedForm(VSponsor(),
|
||||
ad = VByName("fullname"),
|
||||
colliding_ad=VAdByCodename(("codename", "fullname")),
|
||||
codename = VLength("codename", max_length = 100),
|
||||
imgurl = VLength("imgurl", max_length = 1000),
|
||||
linkurl = VLength("linkurl", max_length = 1000))
|
||||
def POST_editad(self, form, jquery, ad, colliding_ad, codename,
|
||||
imgurl, linkurl):
|
||||
if form.has_errors(("codename", "imgurl", "linkurl"),
|
||||
errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors(("codename"), errors.INVALID_OPTION):
|
||||
form.set_html(".status", "some other ad has that codename")
|
||||
pass
|
||||
|
||||
if form.has_error():
|
||||
return
|
||||
|
||||
if ad is None:
|
||||
Ad._new(codename, imgurl, linkurl)
|
||||
form.set_html(".status", "saved. reload to see it.")
|
||||
return
|
||||
|
||||
ad.codename = codename
|
||||
ad.imgurl = imgurl
|
||||
ad.linkurl = linkurl
|
||||
ad._commit()
|
||||
form.set_html(".status", _('saved'))
|
||||
|
||||
@validatedForm(VSponsor(),
|
||||
ad = VByName("fullname"),
|
||||
sr = VSubmitSR("community"),
|
||||
weight = VInt("weight",
|
||||
coerce=False, min=0, max=100000),
|
||||
)
|
||||
def POST_assignad(self, form, jquery, ad, sr, weight):
|
||||
if form.has_errors("ad", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors("community", errors.SUBREDDIT_REQUIRED,
|
||||
errors.SUBREDDIT_NOEXIST, errors.SUBREDDIT_NOTALLOWED):
|
||||
pass
|
||||
|
||||
if form.has_errors("fullname", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors("weight", errors.BAD_NUMBER):
|
||||
pass
|
||||
|
||||
if form.has_error():
|
||||
return
|
||||
|
||||
if ad.codename == "DART" and sr.name == g.default_sr and weight != 100:
|
||||
log_text("Bad default DART weight",
|
||||
"The default DART weight can only be 100, not %s."
|
||||
% weight,
|
||||
"error")
|
||||
abort(403, 'forbidden')
|
||||
|
||||
existing = AdSR.by_ad_and_sr(ad, sr)
|
||||
|
||||
if weight is not None:
|
||||
if existing:
|
||||
existing.weight = weight
|
||||
existing._commit()
|
||||
else:
|
||||
AdSR._new(ad, sr, weight)
|
||||
|
||||
form.set_html(".status", _('saved'))
|
||||
|
||||
else:
|
||||
if existing:
|
||||
existing._delete()
|
||||
AdSR.by_ad(ad, _update=True)
|
||||
AdSR.by_sr(sr, _update=True)
|
||||
|
||||
form.set_html(".status", _('deleted'))
|
||||
|
||||
|
||||
@validatedForm(VAdmin(),
|
||||
award = VByName("fullname"),
|
||||
colliding_award=VAwardByCodename(("codename", "fullname")),
|
||||
@@ -1448,10 +1568,8 @@ class ApiController(RedditController):
|
||||
if form.has_errors("award", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors("recipient", errors.USER_DOESNT_EXIST):
|
||||
pass
|
||||
|
||||
if form.has_errors("recipient", errors.NO_USER):
|
||||
if form.has_errors("recipient", errors.USER_DOESNT_EXIST,
|
||||
errors.NO_USER):
|
||||
pass
|
||||
|
||||
if form.has_errors("fullname", errors.NO_TEXT):
|
||||
@@ -1488,6 +1606,7 @@ class ApiController(RedditController):
|
||||
return self.abort404()
|
||||
recipient = trophy._thing1
|
||||
award = trophy._thing2
|
||||
|
||||
trophy._delete()
|
||||
Trophy.by_account(recipient, _update=True)
|
||||
Trophy.by_award(award, _update=True)
|
||||
@@ -1566,7 +1685,7 @@ class ApiController(RedditController):
|
||||
"%s_%s" % (s._fullname, s.sponsorship_name))
|
||||
|
||||
|
||||
@json_validate(query = nop('query'))
|
||||
@json_validate(query = VPrintable('query', max_length = 50))
|
||||
def POST_search_reddit_names(self, query):
|
||||
names = []
|
||||
if query:
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from reddit_base import RedditController
|
||||
from reddit_base import RedditController, MinimalController, make_key
|
||||
from r2.lib.pages import Button, ButtonNoBody, ButtonEmbed, ButtonLite, \
|
||||
ButtonDemoPanel, WidgetDemoPanel, Bookmarklets, BoringPage
|
||||
from r2.lib.pages.things import wrap_links
|
||||
@@ -31,6 +31,55 @@ from pylons.i18n import _
|
||||
from r2.lib.filters import spaceCompress
|
||||
from r2.controllers.listingcontroller import ListingController
|
||||
|
||||
class ButtonjsController(MinimalController):
|
||||
def pre(self):
|
||||
MinimalController.pre(self)
|
||||
# override user loggedin behavior to ensure this page always
|
||||
# uses the page cache
|
||||
(user, maybe_admin) = \
|
||||
valid_cookie(c.cookies[g.login_cookie].value
|
||||
if g.login_cookie in c.cookies
|
||||
else '')
|
||||
if user:
|
||||
self.user_is_loggedin = True
|
||||
|
||||
@validate(buttontype = VInt('t', 1, 5),
|
||||
url = VSanitizedUrl("url"),
|
||||
_height = VInt('height', 0, 300),
|
||||
_width = VInt('width', 0, 800),
|
||||
autohide = VBoolean("autohide"))
|
||||
def GET_button_embed(self, buttontype, _height, _width, url, autohide):
|
||||
# no buttons on domain listings
|
||||
if isinstance(c.site, DomainSR):
|
||||
return self.abort404()
|
||||
c.render_style = 'js'
|
||||
c.response_content_type = 'text/javascript; charset=UTF-8'
|
||||
if not c.user_is_loggedin and autohide:
|
||||
c.response.content = "void(0);"
|
||||
return c.response
|
||||
|
||||
buttontype = buttontype or 1
|
||||
width, height = ((120, 22), (51, 69), (69, 52),
|
||||
(51, 52), (600, 52))[min(buttontype - 1, 4)]
|
||||
if _width: width = _width
|
||||
if _height: height = _height
|
||||
|
||||
bjs = ButtonEmbed(button=buttontype,
|
||||
width=width,
|
||||
height=height,
|
||||
url = url,
|
||||
referer = request.referer).render()
|
||||
return self.sendjs(bjs, callback='', escape=False)
|
||||
|
||||
def request_key(self):
|
||||
return make_key('button_request_key',
|
||||
c.lang,
|
||||
c.content_langs,
|
||||
request.host,
|
||||
c.cname,
|
||||
request.referer,
|
||||
request.fullpath)
|
||||
|
||||
class ButtonsController(RedditController):
|
||||
def buttontype(self):
|
||||
b = request.get.get('t') or 1
|
||||
@@ -83,7 +132,6 @@ class ButtonsController(RedditController):
|
||||
width = VInt('width', 0, 800),
|
||||
l = VByName('id'))
|
||||
def GET_button_content(self, url, title, css, vote, newwindow, width, l):
|
||||
|
||||
# no buttons on domain listings
|
||||
if isinstance(c.site, DomainSR):
|
||||
c.site = Default
|
||||
@@ -108,12 +156,9 @@ class ButtonsController(RedditController):
|
||||
button = self.buttontype(), **kw)
|
||||
|
||||
l = self.get_wrapped_link(url, l, wrapper)
|
||||
res = l.render()
|
||||
c.response.content = spaceCompress(res)
|
||||
return c.response
|
||||
return l.render()
|
||||
|
||||
|
||||
|
||||
@validate(buttontype = VInt('t', 1, 5),
|
||||
url = VSanitizedUrl("url"),
|
||||
_height = VInt('height', 0, 300),
|
||||
@@ -191,5 +236,3 @@ class ButtonsController(RedditController):
|
||||
return BoringPage(_("bookmarklets"),
|
||||
show_sidebar = False,
|
||||
content=Bookmarklets()).render()
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ from r2.lib.filters import safemarkdown, unsafe
|
||||
try:
|
||||
# place all r2 specific imports in here. If there is a code error, it'll get caught and
|
||||
# the stack trace won't be presented to the user in production
|
||||
from reddit_base import RedditController
|
||||
from reddit_base import RedditController, Cookies
|
||||
from r2.models.subreddit import Default, Subreddit
|
||||
from r2.models.link import Link
|
||||
from r2.lib import pages
|
||||
@@ -122,7 +122,7 @@ class ErrorController(RedditController):
|
||||
c.site.name)
|
||||
message = (strings.banned_subreddit %
|
||||
dict(link = '/message/compose?to=%s&subject=%s' %
|
||||
(g.admin_message_acct,
|
||||
(url_escape(g.admin_message_acct),
|
||||
url_escape(subject))))
|
||||
|
||||
res = pages.RedditError(_('this reddit has been banned'),
|
||||
@@ -146,8 +146,8 @@ class ErrorController(RedditController):
|
||||
|
||||
def GET_document(self):
|
||||
try:
|
||||
#no cookies on errors
|
||||
c.cookies.clear()
|
||||
# clear cookies the old fashioned way
|
||||
c.cookies = Cookies()
|
||||
|
||||
code = request.GET.get('code', '')
|
||||
srname = request.GET.get('srname', '')
|
||||
@@ -155,7 +155,7 @@ class ErrorController(RedditController):
|
||||
if srname:
|
||||
c.site = Subreddit._by_name(srname)
|
||||
if c.render_style not in self.allowed_render_styles:
|
||||
return str(code)
|
||||
return str(int(code))
|
||||
elif takedown and code == '404':
|
||||
link = Link._by_fullname(takedown)
|
||||
return pages.TakedownPage(link).render()
|
||||
|
||||
34
r2/r2/controllers/errorlog.py
Normal file
34
r2/r2/controllers/errorlog.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of the
|
||||
# Original Code is CondeNet, Inc.
|
||||
#
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from pylons import request, g
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.pages import AdminPage, AdminErrorLog
|
||||
from validator import *
|
||||
|
||||
class ErrorlogController(RedditController):
|
||||
@validate(VAdmin())
|
||||
def GET_index(self):
|
||||
res = AdminPage(content = AdminErrorLog(),
|
||||
title = 'error log',
|
||||
show_sidebar = False
|
||||
).render()
|
||||
return res
|
||||
@@ -24,8 +24,8 @@ from pylons.i18n import _
|
||||
from copy import copy
|
||||
|
||||
error_list = dict((
|
||||
('USER_REQUIRED', _("please login to do that")),
|
||||
('VERIFIED_USER_REQUIRED', _("you need to set a valid email address to do that.")),
|
||||
('USER_REQUIRED', _("please login to do that")),
|
||||
('VERIFIED_USER_REQUIRED', _("you need to set a valid email address to do that.")),
|
||||
('NO_URL', _('a url is required')),
|
||||
('BAD_URL', _('you should check that url')),
|
||||
('BAD_CAPTCHA', _('care to try these again?')),
|
||||
@@ -33,9 +33,10 @@ error_list = dict((
|
||||
('USERNAME_TAKEN', _('that username is already taken')),
|
||||
('NO_THING_ID', _('id not specified')),
|
||||
('NOT_AUTHOR', _("you can't do that")),
|
||||
('DELETED_LINK', _('the link you are commenting on has been deleted')),
|
||||
('DELETED_COMMENT', _('that comment has been deleted')),
|
||||
('DELETED_THING', _('that element has been deleted.')),
|
||||
('BAD_PASSWORD', _('invalid password')),
|
||||
('DELETED_THING', _('that element has been deleted')),
|
||||
('BAD_PASSWORD', _('that password is unacceptable')),
|
||||
('WRONG_PASSWORD', _('invalid password')),
|
||||
('BAD_PASSWORD_MATCH', _('passwords do not match')),
|
||||
('NO_NAME', _('please enter a name')),
|
||||
@@ -47,6 +48,7 @@ error_list = dict((
|
||||
('NO_USER', _('please enter a username')),
|
||||
('INVALID_PREF', "that preference isn't valid"),
|
||||
('BAD_NUMBER', _("that number isn't in the right range (%(min)d to %(max)d)")),
|
||||
('BAD_STRING', _("you used a character here that we can't handle")),
|
||||
('BAD_BID', _("your bid must be at least $%(min)d per day and no more than to $%(max)d in total.")),
|
||||
('ALREADY_SUB', _("that link has already been submitted")),
|
||||
('SUBREDDIT_EXISTS', _('that reddit already exists')),
|
||||
@@ -58,7 +60,6 @@ error_list = dict((
|
||||
('EXPIRED', _('your session has expired')),
|
||||
('DRACONIAN', _('you must accept the terms first')),
|
||||
('BANNED_IP', "IP banned"),
|
||||
('BANNED_DOMAIN', "Domain banned"),
|
||||
('BAD_CNAME', "that domain isn't going to work"),
|
||||
('USED_CNAME', "that domain is already in use"),
|
||||
('INVALID_OPTION', _('that option is not valid')),
|
||||
|
||||
@@ -89,13 +89,15 @@ class FrontController(RedditController):
|
||||
else:
|
||||
links = list(links)[:g.num_serendipity]
|
||||
|
||||
rand.shuffle(links)
|
||||
|
||||
builder = IDBuilder(links, skip = True,
|
||||
keep_fn = lambda x: x.fresh,
|
||||
num = g.num_serendipity)
|
||||
num = 1)
|
||||
links = builder.get_items()[0]
|
||||
|
||||
if links:
|
||||
l = rand.choice(links)
|
||||
l = links[0]
|
||||
return self.redirect(add_sr("/tb/" + l._id36))
|
||||
else:
|
||||
return self.redirect(add_sr('/'))
|
||||
@@ -274,8 +276,12 @@ class FrontController(RedditController):
|
||||
content.append(FriendList())
|
||||
elif location == 'update':
|
||||
content = PrefUpdate()
|
||||
elif location == 'feeds' and c.user.pref_private_feeds:
|
||||
content = PrefFeeds()
|
||||
elif location == 'delete':
|
||||
content = PrefDelete()
|
||||
else:
|
||||
return self.abort404()
|
||||
|
||||
return PrefsPage(content = content, infotext=infotext).render()
|
||||
|
||||
@@ -310,7 +316,7 @@ class FrontController(RedditController):
|
||||
|
||||
# moderator is either reddit's moderator or an admin
|
||||
is_moderator = c.user_is_loggedin and c.site.is_moderator(c.user) or c.user_is_admin
|
||||
|
||||
extension_handling = False
|
||||
if is_moderator and location == 'edit':
|
||||
pane = PaneStack()
|
||||
if created == 'true':
|
||||
@@ -320,8 +326,11 @@ class FrontController(RedditController):
|
||||
pane = ModList(editable = is_moderator)
|
||||
elif is_moderator and location == 'banned':
|
||||
pane = BannedList(editable = is_moderator)
|
||||
elif location == 'contributors' and c.site.type != 'public':
|
||||
pane = ContributorList(editable = is_moderator)
|
||||
elif (location == 'contributors' and
|
||||
(c.site.type != 'public' or
|
||||
(c.user_is_loggedin and c.site.use_whitelist and
|
||||
(c.site.is_moderator(c.user) or c.user_is_admin)))):
|
||||
pane = ContributorList(editable = is_moderator)
|
||||
elif (location == 'stylesheet'
|
||||
and c.site.can_change_stylesheet(c.user)
|
||||
and not g.css_killswitch):
|
||||
@@ -338,18 +347,35 @@ class FrontController(RedditController):
|
||||
else c.site.get_spam())
|
||||
builder_cls = (QueryBuilder if isinstance(query, thing.Query)
|
||||
else IDBuilder)
|
||||
def keep_fn(x):
|
||||
# no need to bother mods with banned users, or deleted content
|
||||
if x.hidden or x._deleted:
|
||||
return False
|
||||
if location == "reports" and not x._spam:
|
||||
return (x.reported > 0)
|
||||
if location == "spam":
|
||||
return x._spam
|
||||
return True
|
||||
|
||||
builder = builder_cls(query,
|
||||
skip = True,
|
||||
num = num, after = after,
|
||||
keep_fn = keep_fn,
|
||||
count = count, reverse = reverse,
|
||||
wrap = ListingController.builder_wrapper)
|
||||
listing = LinkListing(builder)
|
||||
pane = listing.listing()
|
||||
if c.user.pref_private_feeds:
|
||||
extension_handling = "private"
|
||||
elif is_moderator and location == 'traffic':
|
||||
pane = RedditTraffic()
|
||||
elif c.user_is_sponsor and location == 'ads':
|
||||
pane = RedditAds()
|
||||
else:
|
||||
return self.abort404()
|
||||
|
||||
return EditReddit(content = pane).render()
|
||||
return EditReddit(content = pane,
|
||||
extension_handling = extension_handling).render()
|
||||
|
||||
def GET_awards(self):
|
||||
"""The awards page."""
|
||||
@@ -517,51 +543,50 @@ class FrontController(RedditController):
|
||||
|
||||
return builder.total_num, timing, res
|
||||
|
||||
def GET_login(self):
|
||||
@validate(dest = VDestination())
|
||||
def GET_login(self, dest):
|
||||
"""The /login form. No link to this page exists any more on
|
||||
the site (all actions invoking it now go through the login
|
||||
cover). However, this page is still used for logging the user
|
||||
in during submission or voting from the bookmarklets."""
|
||||
|
||||
# dest is the location to redirect to upon completion
|
||||
dest = request.get.get('dest','') or request.referer or '/'
|
||||
if (c.user_is_loggedin and
|
||||
not request.environ.get('extension') == 'embed'):
|
||||
return self.redirect(dest)
|
||||
return LoginPage(dest = dest).render()
|
||||
|
||||
def GET_logout(self):
|
||||
dest = request.referer or '/'
|
||||
@validate(VUser(),
|
||||
VModhash(),
|
||||
dest = VDestination())
|
||||
def GET_logout(self, dest):
|
||||
return self.redirect(dest)
|
||||
|
||||
@validate(VUser(),
|
||||
VModhash())
|
||||
def POST_logout(self, dest = None):
|
||||
VModhash(),
|
||||
dest = VDestination())
|
||||
def POST_logout(self, dest):
|
||||
"""wipe login cookie and redirect to referer."""
|
||||
self.logout()
|
||||
dest = request.post.get('dest','') or request.referer or '/'
|
||||
return self.redirect(dest)
|
||||
|
||||
|
||||
@validate(VUser())
|
||||
def GET_adminon(self):
|
||||
|
||||
@validate(VUser(),
|
||||
dest = VDestination())
|
||||
def GET_adminon(self, dest):
|
||||
"""Enable admin interaction with site"""
|
||||
#check like this because c.user_is_admin is still false
|
||||
if not c.user.name in g.admins:
|
||||
return self.abort404()
|
||||
self.login(c.user, admin = True)
|
||||
|
||||
dest = request.referer or '/'
|
||||
return self.redirect(dest)
|
||||
|
||||
@validate(VAdmin())
|
||||
def GET_adminoff(self):
|
||||
@validate(VAdmin(),
|
||||
dest = VDestination())
|
||||
def GET_adminoff(self, dest):
|
||||
"""disable admin interaction with site."""
|
||||
if not c.user.name in g.admins:
|
||||
return self.abort404()
|
||||
self.login(c.user, admin = False)
|
||||
|
||||
dest = request.referer or '/'
|
||||
return self.redirect(dest)
|
||||
|
||||
def GET_validuser(self):
|
||||
@@ -604,9 +629,9 @@ class FrontController(RedditController):
|
||||
captcha = Captcha() if c.user.needs_captcha() else None
|
||||
sr_names = (Subreddit.submit_sr_names(c.user) or
|
||||
Subreddit.submit_sr_names(None))
|
||||
|
||||
|
||||
return FormPage(_("submit"),
|
||||
return FormPage(_("submit"),
|
||||
show_sidebar = True,
|
||||
content=NewLink(url=url or '',
|
||||
title=title or '',
|
||||
subreddits = sr_names,
|
||||
@@ -714,7 +739,3 @@ class FrontController(RedditController):
|
||||
def GET_site_traffic(self):
|
||||
return BoringPage("traffic",
|
||||
content = RedditTraffic()).render()
|
||||
|
||||
|
||||
def GET_ad(self, reddit = None):
|
||||
return Dart_Ad(reddit).render(style="html")
|
||||
|
||||
@@ -8,6 +8,8 @@ from pylons import c, g
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.amqp import worker
|
||||
|
||||
from validator import *
|
||||
|
||||
class HealthController(RedditController):
|
||||
def shutdown(self):
|
||||
thread_pool = c.thread_pool
|
||||
@@ -40,9 +42,12 @@ class HealthController(RedditController):
|
||||
c.response.content = "i'm still alive!"
|
||||
return c.response
|
||||
|
||||
def GET_shutdown(self):
|
||||
if not g.allow_shutdown:
|
||||
@validate(secret=nop('secret'))
|
||||
def GET_shutdown(self, secret):
|
||||
if not g.shutdown_secret:
|
||||
self.abort404()
|
||||
if not secret or secret != g.shutdown_secret:
|
||||
self.abort403()
|
||||
|
||||
c.dontcache = True
|
||||
#the will make the next health-check initiate the shutdown
|
||||
|
||||
@@ -122,7 +122,7 @@ class ListingController(RedditController):
|
||||
builder_cls = SearchBuilder
|
||||
elif isinstance(self.query_obj, iters):
|
||||
builder_cls = IDBuilder
|
||||
elif isinstance(self.query_obj, queries.CachedResults):
|
||||
elif isinstance(self.query_obj, (queries.CachedResults, queries.MergedCachedResults)):
|
||||
builder_cls = IDBuilder
|
||||
|
||||
b = builder_cls(self.query_obj,
|
||||
@@ -253,7 +253,7 @@ class HotController(FixListing, ListingController):
|
||||
and not isinstance(c.site, FakeSubreddit)
|
||||
and self.after is None
|
||||
and self.count == 0):
|
||||
return get_hot(c.site, only_fullnames = True)
|
||||
return get_hot([c.site], only_fullnames = True)[0]
|
||||
else:
|
||||
return c.site.get_links('hot', 'all')
|
||||
|
||||
@@ -286,16 +286,6 @@ class SavedController(ListingController):
|
||||
def GET_listing(self, **env):
|
||||
return ListingController.GET_listing(self, **env)
|
||||
|
||||
class ToplinksController(ListingController):
|
||||
where = 'toplinks'
|
||||
title_text = _('top scoring links')
|
||||
|
||||
def query(self):
|
||||
return c.site.get_links('toplinks', 'all')
|
||||
|
||||
def GET_listing(self, **env):
|
||||
return ListingController.GET_listing(self, **env)
|
||||
|
||||
class NewController(ListingController):
|
||||
where = 'new'
|
||||
title_text = _('newest submissions')
|
||||
@@ -503,11 +493,12 @@ class UserController(ListingController):
|
||||
|
||||
class MessageController(ListingController):
|
||||
show_sidebar = False
|
||||
show_nums = False
|
||||
render_cls = MessagePage
|
||||
|
||||
@property
|
||||
def menus(self):
|
||||
if self.where in ('inbox', 'messages', 'comments',
|
||||
if c.default_sr and self.where in ('inbox', 'messages', 'comments',
|
||||
'selfreply', 'unread'):
|
||||
buttons = (NavButton(_("all"), "inbox"),
|
||||
NavButton(_("unread"), "unread"),
|
||||
@@ -517,12 +508,27 @@ class MessageController(ListingController):
|
||||
|
||||
return [NavMenu(buttons, base_path = '/message/',
|
||||
default = 'inbox', type = "flatlist")]
|
||||
elif not c.default_sr or self.where == 'moderator':
|
||||
buttons = (NavButton(_("all"), "inbox"),
|
||||
NavButton(_("unread"), "unread"))
|
||||
return [NavMenu(buttons, base_path = '/message/moderator/',
|
||||
default = 'inbox', type = "flatlist")]
|
||||
return []
|
||||
|
||||
|
||||
def title(self):
|
||||
return _('messages') + ': ' + _(self.where)
|
||||
|
||||
def keep_fn(self):
|
||||
def keep(item):
|
||||
wouldkeep = item.keep_item(item)
|
||||
# don't show user their own unread stuff
|
||||
if ((self.where == 'unread' or self.subwhere == 'unread')
|
||||
and item.author_id == c.user._id):
|
||||
return False
|
||||
return wouldkeep
|
||||
return keep
|
||||
|
||||
@staticmethod
|
||||
def builder_wrapper(thing):
|
||||
if isinstance(thing, Comment):
|
||||
@@ -539,24 +545,32 @@ class MessageController(ListingController):
|
||||
return w
|
||||
|
||||
def builder(self):
|
||||
if self.where == 'messages':
|
||||
if (self.where == 'messages' or
|
||||
(self.where == "moderator" and self.subwhere != "unread")):
|
||||
root = c.user
|
||||
message_cls = UserMessageBuilder
|
||||
if not c.default_sr:
|
||||
root = c.site
|
||||
message_cls = SrMessageBuilder
|
||||
elif self.where == 'moderator' and self.subwhere != 'unread':
|
||||
message_cls = ModeratorMessageBuilder
|
||||
|
||||
parent = None
|
||||
skip = False
|
||||
if self.message:
|
||||
if self.message.first_message:
|
||||
parent = Message._byID(self.message.first_message)
|
||||
else:
|
||||
parent = self.message
|
||||
return MessageBuilder(c.user, parent = parent,
|
||||
skip = False,
|
||||
focal = self.message,
|
||||
wrap = self.builder_wrapper,
|
||||
num = self.num)
|
||||
elif c.user.pref_threaded_messages:
|
||||
skip = (c.render_style == "html")
|
||||
return MessageBuilder(c.user, wrap = self.builder_wrapper,
|
||||
skip = skip,
|
||||
num = self.num,
|
||||
after = self.after,
|
||||
reverse = self.reverse)
|
||||
|
||||
return message_cls(root, wrap = self.builder_wrapper,
|
||||
parent = parent,
|
||||
skip = skip,
|
||||
num = self.num,
|
||||
after = self.after,
|
||||
reverse = self.reverse)
|
||||
return ListingController.builder(self)
|
||||
|
||||
def listing(self):
|
||||
@@ -578,7 +592,22 @@ class MessageController(ListingController):
|
||||
q = queries.get_unread_inbox(c.user)
|
||||
elif self.where == 'sent':
|
||||
q = queries.get_sent(c.user)
|
||||
|
||||
elif self.where == 'moderator' and self.subwhere == 'unread':
|
||||
if c.default_sr:
|
||||
srids = Subreddit.reverse_moderator_ids(c.user)
|
||||
srs = Subreddit._byID(srids, data = False, return_dict = False)
|
||||
q = queries.merge_results(
|
||||
*[queries.get_unread_subreddit_messages(s) for s in srs])
|
||||
else:
|
||||
q = queries.get_unread_subreddit_messages(c.site)
|
||||
elif self.where == 'moderator':
|
||||
if c.have_mod_messages and self.mark != 'false':
|
||||
c.user.modmsgtime = False
|
||||
c.user._commit()
|
||||
# the query is handled by the builder on the moderator page
|
||||
return
|
||||
else:
|
||||
return self.abort404()
|
||||
if self.where != 'sent':
|
||||
#reset the inbox
|
||||
if c.have_messages and self.mark != 'false':
|
||||
@@ -590,11 +619,16 @@ class MessageController(ListingController):
|
||||
@validate(VUser(),
|
||||
message = VMessageID('mid'),
|
||||
mark = VOneOf('mark',('true','false'), default = 'true'))
|
||||
def GET_listing(self, where, mark, message, **env):
|
||||
self.where = where
|
||||
def GET_listing(self, where, mark, message, subwhere = None, **env):
|
||||
if not (c.default_sr or c.site.is_moderator(c.user) or c.user_is_admin):
|
||||
abort(403, "forbidden")
|
||||
if not c.default_sr:
|
||||
self.where = "moderator"
|
||||
else:
|
||||
self.where = where
|
||||
self.subwhere = subwhere
|
||||
self.mark = mark
|
||||
self.message = message
|
||||
c.msg_location = where
|
||||
return ListingController.GET_listing(self, **env)
|
||||
|
||||
@validate(VUser(),
|
||||
@@ -609,7 +643,7 @@ class MessageController(ListingController):
|
||||
message = message,
|
||||
success = success)
|
||||
return MessagePage(content = content).render()
|
||||
|
||||
|
||||
class RedditsController(ListingController):
|
||||
render_cls = SubredditsPage
|
||||
|
||||
@@ -645,7 +679,8 @@ class MyredditsController(ListingController):
|
||||
NavButton(plurals.contributor, 'contributor'),
|
||||
NavButton(plurals.moderator, 'moderator'))
|
||||
|
||||
return [NavMenu(buttons, base_path = '/reddits/mine/', default = 'subscriber', type = "flatlist")]
|
||||
return [NavMenu(buttons, base_path = '/reddits/mine/',
|
||||
default = 'subscriber', type = "flatlist")]
|
||||
|
||||
def title(self):
|
||||
return _('reddits: ') + self.where
|
||||
|
||||
@@ -20,23 +20,24 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from validator import *
|
||||
from reddit_base import RedditController
|
||||
from reddit_base import MinimalController
|
||||
|
||||
from r2.lib.scraper import scrapers
|
||||
from r2.lib.pages import MediaEmbedBody
|
||||
from r2.lib.pages import MediaEmbedBody, ComScore, render_ad
|
||||
|
||||
from pylons import request
|
||||
from pylons.controllers.util import abort
|
||||
|
||||
class MediaembedController(RedditController):
|
||||
class MediaembedController(MinimalController):
|
||||
@validate(link = VLink('link'))
|
||||
def GET_mediaembed(self, link):
|
||||
if request.host != g.media_domain:
|
||||
# don't serve up untrusted content except on our
|
||||
# specifically untrusted domain
|
||||
return self.abort404()
|
||||
abort(404)
|
||||
|
||||
if not link or not link.media_object:
|
||||
return self.abort404()
|
||||
abort(404)
|
||||
|
||||
if isinstance(link.media_object, basestring):
|
||||
# it's an old-style string
|
||||
@@ -50,3 +51,16 @@ class MediaembedController(RedditController):
|
||||
content = media_embed.content
|
||||
|
||||
return MediaEmbedBody(body = content).render()
|
||||
|
||||
def GET_ad(self, reddit_name = None):
|
||||
c.render_style = "html"
|
||||
return render_ad(reddit_name=reddit_name)
|
||||
|
||||
def GET_ad_by_codename(self, codename = None):
|
||||
if not codename:
|
||||
abort(404)
|
||||
c.render_style = "html"
|
||||
return render_ad(codename=codename)
|
||||
|
||||
def GET_comscore(self, reddit = None):
|
||||
return ComScore().render(style="html")
|
||||
|
||||
@@ -29,29 +29,10 @@ from pylons.i18n import _
|
||||
from r2.models import *
|
||||
import sha
|
||||
|
||||
def to_referer(func, **params):
|
||||
def _to_referer(self, *a, **kw):
|
||||
res = func(self, *a, **kw)
|
||||
dest = res.get('redirect') or request.referer or '/'
|
||||
return self.redirect(dest + query_string(params))
|
||||
return _to_referer
|
||||
|
||||
|
||||
class PostController(ApiController):
|
||||
def api_wrapper(self, kw):
|
||||
return Storage(**kw)
|
||||
|
||||
#TODO: feature disabled for now
|
||||
# @to_referer
|
||||
# @validate(VUser(),
|
||||
# key = VOneOf('key', ('pref_bio','pref_location',
|
||||
# 'pref_url')),
|
||||
# value = nop('value'))
|
||||
# def POST_user_desc(self, key, value):
|
||||
# setattr(c.user, key, value)
|
||||
# c.user._commit()
|
||||
# return {}
|
||||
|
||||
def set_options(self, all_langs, pref_lang, **kw):
|
||||
if c.errors.errors:
|
||||
print "fucker"
|
||||
@@ -87,7 +68,9 @@ class PostController(ApiController):
|
||||
self.set_options( all_langs, pref_lang)
|
||||
return self.redirect(request.referer)
|
||||
|
||||
@validate(pref_frame = VBoolean('frame'),
|
||||
@validate(VUser(),
|
||||
VModhash(),
|
||||
pref_frame = VBoolean('frame'),
|
||||
pref_clickgadget = VBoolean('clickgadget'),
|
||||
pref_organic = VBoolean('organic'),
|
||||
pref_newwindow = VBoolean('newwindow'),
|
||||
@@ -110,6 +93,7 @@ class PostController(ApiController):
|
||||
pref_mark_messages_read = VBoolean("mark_messages_read"),
|
||||
pref_threaded_messages = VBoolean("threaded_messages"),
|
||||
pref_collapse_read_messages = VBoolean("collapse_read_messages"),
|
||||
pref_private_feeds = VBoolean("private_feeds"),
|
||||
all_langs = nop('all-langs', default = 'all'))
|
||||
def POST_options(self, all_langs, pref_lang, **kw):
|
||||
#temporary. eventually we'll change pref_clickgadget to an
|
||||
@@ -176,12 +160,12 @@ class PostController(ApiController):
|
||||
msg_hash = msg_hash)).render()
|
||||
|
||||
|
||||
def POST_login(self, *a, **kw):
|
||||
@validate(dest = VDestination(default = "/"))
|
||||
def POST_login(self, dest, *a, **kw):
|
||||
ApiController.POST_login(self, *a, **kw)
|
||||
c.render_style = "html"
|
||||
c.response_content_type = ""
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
errors = list(c.errors)
|
||||
if errors:
|
||||
for e in errors:
|
||||
@@ -190,18 +174,17 @@ class PostController(ApiController):
|
||||
c.errors.remove(e)
|
||||
c.errors.add(e[0], msg)
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
return LoginPage(user_login = request.post.get('user'),
|
||||
dest = dest).render()
|
||||
|
||||
return self.redirect(dest)
|
||||
|
||||
def POST_reg(self, *a, **kw):
|
||||
@validate(dest = VDestination(default = "/"))
|
||||
def POST_reg(self, dest, *a, **kw):
|
||||
ApiController.POST_register(self, *a, **kw)
|
||||
c.render_style = "html"
|
||||
c.response_content_type = ""
|
||||
|
||||
dest = request.post.get('dest', request.referer or '/')
|
||||
errors = list(c.errors)
|
||||
if errors:
|
||||
for e in errors:
|
||||
|
||||
@@ -168,7 +168,7 @@ class PromoteController(ListingController):
|
||||
promote.reject_promo(thing, reason = reason)
|
||||
# also reject anything that is live but has a reason given
|
||||
elif (c.user_is_sponsor and reason and
|
||||
thing.promte_status == promote.STATUS.promoted):
|
||||
thing.promote_status == promote.STATUS.promoted):
|
||||
promote.reject_promo(thing, reason = reason)
|
||||
# otherwise, mark it as "finished"
|
||||
else:
|
||||
@@ -220,6 +220,9 @@ class PromoteController(ListingController):
|
||||
# want the URL
|
||||
url = url[0].url
|
||||
|
||||
if form.has_errors('bid', errors.BAD_BID):
|
||||
return
|
||||
|
||||
# check dates and date range
|
||||
start, end = [x.date() for x in dates] if dates else (None, None)
|
||||
if (not l or
|
||||
@@ -242,7 +245,6 @@ class PromoteController(ListingController):
|
||||
if (form.has_errors('title', errors.NO_TEXT,
|
||||
errors.TOO_LONG) or
|
||||
form.has_errors('url', errors.NO_URL, errors.BAD_URL) or
|
||||
form.has_errors('bid', errors.BAD_BID) or
|
||||
(not l and jquery.has_errors('ratelimit', errors.RATELIMIT))):
|
||||
return
|
||||
elif l:
|
||||
|
||||
@@ -25,11 +25,11 @@ from pylons.controllers.util import abort, redirect_to
|
||||
from pylons.i18n import _
|
||||
from pylons.i18n.translation import LanguageError
|
||||
from r2.lib.base import BaseController, proxyurl
|
||||
from r2.lib import pages, utils, filters
|
||||
from r2.lib import pages, utils, filters, amqp
|
||||
from r2.lib.utils import http_utils, UniqueIterator
|
||||
from r2.lib.cache import LocalCache
|
||||
from r2.lib.cache import LocalCache, make_key, MemcachedError
|
||||
import random as rand
|
||||
from r2.models.account import valid_cookie, FakeAccount
|
||||
from r2.models.account import valid_cookie, FakeAccount, valid_feed
|
||||
from r2.models.subreddit import Subreddit
|
||||
from r2.models import *
|
||||
from errors import ErrorSet
|
||||
@@ -37,12 +37,14 @@ from validator import *
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.jsontemplates import api_type
|
||||
|
||||
from Cookie import CookieError
|
||||
from copy import copy
|
||||
from Cookie import CookieError
|
||||
from datetime import datetime
|
||||
import sha, simplejson, locale
|
||||
from hashlib import sha1, md5
|
||||
from urllib import quote, unquote
|
||||
from simplejson import dumps
|
||||
import simplejson
|
||||
import locale
|
||||
|
||||
from r2.lib.tracking import encrypt, decrypt
|
||||
|
||||
@@ -224,7 +226,7 @@ def over18():
|
||||
else:
|
||||
if 'over18' in c.cookies:
|
||||
cookie = c.cookies['over18'].value
|
||||
if cookie == sha.new(request.ip).hexdigest():
|
||||
if cookie == sha1(request.ip).hexdigest():
|
||||
return True
|
||||
|
||||
def set_subreddit():
|
||||
@@ -281,6 +283,13 @@ def set_content_type():
|
||||
return utils.to_js(content,callback = request.params.get(
|
||||
"callback", "document.write"))
|
||||
c.response_wrappers.append(to_js)
|
||||
if ext in ("rss", "api", "json") and request.method.upper() == "GET":
|
||||
user = valid_feed(request.GET.get("user"),
|
||||
request.GET.get("feed"),
|
||||
request.path)
|
||||
if user:
|
||||
c.user = user
|
||||
c.user_is_loggedin = True
|
||||
|
||||
def get_browser_langs():
|
||||
browser_langs = []
|
||||
@@ -403,6 +412,7 @@ def ratelimit_throttled():
|
||||
if throttled(ip) or throttled(subnet):
|
||||
abort(503, 'service temporarily unavailable')
|
||||
|
||||
|
||||
#TODO i want to get rid of this function. once the listings in front.py are
|
||||
#moved into listingcontroller, we shouldn't have a need for this
|
||||
#anymore
|
||||
@@ -411,13 +421,16 @@ def base_listing(fn):
|
||||
after = VByName('after'),
|
||||
before = VByName('before'),
|
||||
count = VCount('count'),
|
||||
target = VTarget("target"))
|
||||
target = VTarget("target"),
|
||||
show = VLength('show', 3))
|
||||
def new_fn(self, before, **env):
|
||||
if c.render_style == "htmllite":
|
||||
c.link_target = env.get("target")
|
||||
elif "target" in env:
|
||||
del env["target"]
|
||||
|
||||
if "show" in env and env['show'] == 'all':
|
||||
c.ignore_hide_rules = True
|
||||
kw = build_arg_list(fn, env)
|
||||
|
||||
#turn before into after/reverse
|
||||
@@ -429,40 +442,32 @@ def base_listing(fn):
|
||||
return fn(self, **kw)
|
||||
return new_fn
|
||||
|
||||
class RedditController(BaseController):
|
||||
class MinimalController(BaseController):
|
||||
|
||||
def request_key(self):
|
||||
# note that this references the cookie at request time, not
|
||||
# the current value of it
|
||||
cookie_keys = []
|
||||
for x in cache_affecting_cookies:
|
||||
cookie_keys.append(request.cookies.get(x,''))
|
||||
try:
|
||||
cookies_key = [(x, request.cookies.get(x,''))
|
||||
for x in cache_affecting_cookies]
|
||||
except CookieError:
|
||||
cookies_key = ''
|
||||
|
||||
key = ''.join((str(c.lang),
|
||||
str(c.content_langs),
|
||||
request.host,
|
||||
str(c.cname),
|
||||
str(request.fullpath),
|
||||
str(c.over18),
|
||||
str(c.firsttime),
|
||||
''.join(cookie_keys)))
|
||||
return key
|
||||
return make_key('request_key',
|
||||
c.lang,
|
||||
c.content_langs,
|
||||
request.host,
|
||||
c.cname,
|
||||
request.fullpath,
|
||||
c.over18,
|
||||
c.firsttime,
|
||||
cookies_key)
|
||||
|
||||
def cached_response(self):
|
||||
return c.response
|
||||
|
||||
@staticmethod
|
||||
def login(user, admin = False, rem = False):
|
||||
c.cookies[g.login_cookie] = Cookie(value = user.make_cookie(admin = admin),
|
||||
expires = NEVER if rem else None)
|
||||
|
||||
@staticmethod
|
||||
def logout(admin = False):
|
||||
c.cookies[g.login_cookie] = Cookie(value='')
|
||||
|
||||
def pre(self):
|
||||
c.start_time = datetime.now(g.tz)
|
||||
|
||||
g.reset_caches()
|
||||
|
||||
c.domain_prefix = request.environ.get("reddit-domain-prefix",
|
||||
@@ -474,10 +479,106 @@ class RedditController(BaseController):
|
||||
|
||||
# the domain has to be set before Cookies get initialized
|
||||
set_subreddit()
|
||||
c.errors = ErrorSet()
|
||||
c.cookies = Cookies()
|
||||
|
||||
def try_pagecache(self):
|
||||
#check content cache
|
||||
if not c.user_is_loggedin:
|
||||
r = g.rendercache.get(self.request_key())
|
||||
if r and request.method == 'GET':
|
||||
response = c.response
|
||||
response.headers = r.headers
|
||||
response.content = r.content
|
||||
|
||||
for x in r.cookies.keys():
|
||||
if x in cache_affecting_cookies:
|
||||
cookie = r.cookies[x]
|
||||
response.set_cookie(key = x,
|
||||
value = cookie.value,
|
||||
domain = cookie.get('domain',None),
|
||||
expires = cookie.get('expires',None),
|
||||
path = cookie.get('path',None))
|
||||
|
||||
response.status_code = r.status_code
|
||||
request.environ['pylons.routes_dict']['action'] = 'cached_response'
|
||||
# make sure to carry over the content type
|
||||
c.response_content_type = r.headers['content-type']
|
||||
if r.headers.has_key('access-control'):
|
||||
c.response_access_control = r.headers['access-control']
|
||||
c.used_cache = True
|
||||
# response wrappers have already been applied before cache write
|
||||
c.response_wrappers = []
|
||||
|
||||
|
||||
def post(self):
|
||||
response = c.response
|
||||
content = filter(None, response.content)
|
||||
if isinstance(content, (list, tuple)):
|
||||
content = ''.join(content)
|
||||
for w in c.response_wrappers:
|
||||
content = w(content)
|
||||
response.content = content
|
||||
if c.response_content_type:
|
||||
response.headers['Content-Type'] = c.response_content_type
|
||||
if c.response_access_control:
|
||||
c.response.headers['Access-Control'] = c.response_access_control
|
||||
|
||||
if c.user_is_loggedin:
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
|
||||
# send cookies
|
||||
if not c.used_cache and c.cookies:
|
||||
# if we used the cache, these cookies should be set by the
|
||||
# cached response object instead
|
||||
for k,v in c.cookies.iteritems():
|
||||
if v.dirty:
|
||||
response.set_cookie(key = k,
|
||||
value = quote(v.value),
|
||||
domain = v.domain,
|
||||
expires = v.expires)
|
||||
|
||||
#return
|
||||
#set content cache
|
||||
if (g.page_cache_time
|
||||
and request.method == 'GET'
|
||||
and not c.user_is_loggedin
|
||||
and not c.used_cache
|
||||
and not c.dontcache
|
||||
and response.status_code != 503
|
||||
and response.content and response.content[0]):
|
||||
try:
|
||||
g.rendercache.set(self.request_key(),
|
||||
response,
|
||||
g.page_cache_time)
|
||||
except MemcachedError:
|
||||
# the key was too big to set in the rendercache
|
||||
g.log.debug("Ignored too-big render cache")
|
||||
|
||||
if g.enable_usage_stats:
|
||||
amqp.add_kw("usage_q",
|
||||
start_time = c.start_time,
|
||||
end_time = datetime.now(g.tz),
|
||||
action = str(c.action) or "static")
|
||||
|
||||
class RedditController(MinimalController):
|
||||
|
||||
@staticmethod
|
||||
def login(user, admin = False, rem = False):
|
||||
c.cookies[g.login_cookie] = Cookie(value = user.make_cookie(admin = admin),
|
||||
expires = NEVER if rem else None)
|
||||
|
||||
@staticmethod
|
||||
def logout(admin = False):
|
||||
c.cookies[g.login_cookie] = Cookie(value='')
|
||||
|
||||
def pre(self):
|
||||
MinimalController.pre(self)
|
||||
|
||||
set_cnameframe()
|
||||
|
||||
# populate c.cookies unless we're on the unsafe media_domain
|
||||
c.cookies = Cookies()
|
||||
if request.host != g.media_domain or g.media_domain == g.domain:
|
||||
try:
|
||||
for k,v in request.cookies.iteritems():
|
||||
@@ -489,7 +590,6 @@ class RedditController(BaseController):
|
||||
request.environ['HTTP_COOKIE'] = ''
|
||||
|
||||
c.response_wrappers = []
|
||||
c.errors = ErrorSet()
|
||||
c.firsttime = firsttime()
|
||||
(c.user, maybe_admin) = \
|
||||
valid_cookie(c.cookies[g.login_cookie].value
|
||||
@@ -515,6 +615,12 @@ class RedditController(BaseController):
|
||||
read_mod_cookie()
|
||||
if hasattr(c.user, 'msgtime') and c.user.msgtime:
|
||||
c.have_messages = c.user.msgtime
|
||||
if hasattr(c.user, 'modmsgtime'):
|
||||
c.show_mod_mail = True
|
||||
if c.user.modmsgtime:
|
||||
c.have_mod_messages = c.user.modmsgtime
|
||||
else:
|
||||
c.show_mod_mail = Subreddit.reverse_moderator_ids(c.user)
|
||||
c.user_is_admin = maybe_admin and c.user.name in g.admins
|
||||
c.user_is_sponsor = c.user_is_admin or c.user.name in g.sponsors
|
||||
if not g.disallow_db_writes:
|
||||
@@ -560,74 +666,6 @@ class RedditController(BaseController):
|
||||
elif c.site.domain and c.site.css_on_cname and not c.cname:
|
||||
c.allow_styles = False
|
||||
|
||||
#check content cache
|
||||
if not c.user_is_loggedin:
|
||||
r = g.rendercache.get(self.request_key())
|
||||
if r and request.method == 'GET':
|
||||
response = c.response
|
||||
response.headers = r.headers
|
||||
response.content = r.content
|
||||
|
||||
for x in r.cookies.keys():
|
||||
if x in cache_affecting_cookies:
|
||||
cookie = r.cookies[x]
|
||||
response.set_cookie(key = x,
|
||||
value = cookie.value,
|
||||
domain = cookie.get('domain',None),
|
||||
expires = cookie.get('expires',None),
|
||||
path = cookie.get('path',None))
|
||||
|
||||
response.status_code = r.status_code
|
||||
request.environ['pylons.routes_dict']['action'] = 'cached_response'
|
||||
# make sure to carry over the content type
|
||||
c.response_content_type = r.headers['content-type']
|
||||
if r.headers.has_key('access-control'):
|
||||
c.response_access_control = r.headers['access-control']
|
||||
c.used_cache = True
|
||||
# response wrappers have already been applied before cache write
|
||||
c.response_wrappers = []
|
||||
|
||||
def post(self):
|
||||
response = c.response
|
||||
content = filter(None, response.content)
|
||||
if isinstance(content, (list, tuple)):
|
||||
content = ''.join(content)
|
||||
for w in c.response_wrappers:
|
||||
content = w(content)
|
||||
response.content = content
|
||||
if c.response_content_type:
|
||||
response.headers['Content-Type'] = c.response_content_type
|
||||
if c.response_access_control:
|
||||
c.response.headers['Access-Control'] = c.response_access_control
|
||||
|
||||
if c.user_is_loggedin:
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers['Pragma'] = 'no-cache'
|
||||
|
||||
# send cookies
|
||||
if not c.used_cache and c.cookies:
|
||||
# if we used the cache, these cookies should be set by the
|
||||
# cached response object instead
|
||||
for k,v in c.cookies.iteritems():
|
||||
if v.dirty:
|
||||
response.set_cookie(key = k,
|
||||
value = quote(v.value),
|
||||
domain = v.domain,
|
||||
expires = v.expires)
|
||||
|
||||
#return
|
||||
#set content cache
|
||||
if (g.page_cache_time
|
||||
and request.method == 'GET'
|
||||
and not c.user_is_loggedin
|
||||
and not c.used_cache
|
||||
and not c.dontcache
|
||||
and response.status_code != 503
|
||||
and response.content and response.content[0]):
|
||||
g.rendercache.set(self.request_key(),
|
||||
response,
|
||||
g.page_cache_time)
|
||||
|
||||
def check_modified(self, thing, action):
|
||||
if c.user_is_loggedin:
|
||||
return
|
||||
@@ -644,6 +682,9 @@ class RedditController(BaseController):
|
||||
def abort404(self):
|
||||
abort(404, "not found")
|
||||
|
||||
def abort403(self):
|
||||
abort(403, "forbidden")
|
||||
|
||||
def sendpng(self, string):
|
||||
c.response_content_type = 'image/png'
|
||||
c.response.content = string
|
||||
@@ -661,7 +702,7 @@ class RedditController(BaseController):
|
||||
return request.path + utils.query_string(merged)
|
||||
|
||||
def api_wrapper(self, kw):
|
||||
data = dumps(kw)
|
||||
data = simplejson.dumps(kw)
|
||||
if request.method == "GET" and request.GET.get("callback"):
|
||||
return "%s(%s)" % (websafe_json(request.GET.get("callback")),
|
||||
websafe_json(data))
|
||||
|
||||
34
r2/r2/controllers/usage.py
Normal file
34
r2/r2/controllers/usage.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# The contents of this file are subject to the Common Public Attribution
|
||||
# License Version 1.0. (the "License"); you may not use this file except in
|
||||
# compliance with the License. You may obtain a copy of the License at
|
||||
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
# software over a computer network and provide for limited attribution for the
|
||||
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
# with Exhibit B.
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
# the specific language governing rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is Reddit.
|
||||
#
|
||||
# The Original Developer is the Initial Developer. The Initial Developer of the
|
||||
# Original Code is CondeNet, Inc.
|
||||
#
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from pylons import request, g
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.pages import AdminPage, AdminUsage
|
||||
from validator import *
|
||||
|
||||
class UsageController(RedditController):
|
||||
|
||||
@validate(VAdmin())
|
||||
def GET_index(self):
|
||||
res = AdminPage(content = AdminUsage(),
|
||||
show_sidebar = False,
|
||||
title = 'usage').render()
|
||||
return res
|
||||
@@ -24,11 +24,12 @@ from pylons.i18n import _
|
||||
from pylons.controllers.util import abort
|
||||
from r2.lib import utils, captcha, promote
|
||||
from r2.lib.filters import unkeep_space, websafe, _force_unicode
|
||||
from r2.lib.filters import markdown_souptest
|
||||
from r2.lib.db.operators import asc, desc
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.jsonresponse import json_respond, JQueryResponse, JsonResponse
|
||||
from r2.lib.jsontemplates import api_type
|
||||
|
||||
from r2.lib.log import log_text
|
||||
from r2.models import *
|
||||
from r2.lib.authorize import Address, CreditCard
|
||||
|
||||
@@ -37,6 +38,7 @@ from r2.controllers.errors import VerifiedUserRequiredException
|
||||
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
from curses.ascii import isprint
|
||||
import re, inspect
|
||||
import pycountry
|
||||
|
||||
@@ -123,7 +125,6 @@ def _make_validated_kw(fn, simple_vals, param_vals, env):
|
||||
for var, validator in param_vals.iteritems():
|
||||
kw[var] = validator(env)
|
||||
return kw
|
||||
|
||||
|
||||
def validate(*simple_vals, **param_vals):
|
||||
def val(fn):
|
||||
@@ -230,7 +231,7 @@ class VRequired(Validator):
|
||||
if not e: e = self._error
|
||||
if e:
|
||||
self.set_error(e)
|
||||
|
||||
|
||||
def run(self, item):
|
||||
if not item:
|
||||
self.error()
|
||||
@@ -266,6 +267,25 @@ class VCommentByID(VThing):
|
||||
def __init__(self, param, redirect = True, *a, **kw):
|
||||
VThing.__init__(self, param, Comment, redirect=redirect, *a, **kw)
|
||||
|
||||
class VAd(VThing):
|
||||
def __init__(self, param, redirect = True, *a, **kw):
|
||||
VThing.__init__(self, param, Ad, redirect=redirect, *a, **kw)
|
||||
|
||||
class VAdByCodename(Validator):
|
||||
def run(self, codename, required_fullname=None):
|
||||
if not codename:
|
||||
return self.set_error(errors.NO_TEXT)
|
||||
|
||||
try:
|
||||
a = Ad._by_codename(codename)
|
||||
except NotFound:
|
||||
a = None
|
||||
|
||||
if a and required_fullname and a._fullname != required_fullname:
|
||||
return self.set_error(errors.INVALID_OPTION)
|
||||
else:
|
||||
return a
|
||||
|
||||
class VAward(VThing):
|
||||
def __init__(self, param, redirect = True, *a, **kw):
|
||||
VThing.__init__(self, param, Award, redirect=redirect, *a, **kw)
|
||||
@@ -314,7 +334,7 @@ class VMessageID(Validator):
|
||||
try:
|
||||
cid = int(cid, 36)
|
||||
m = Message._byID(cid, True)
|
||||
if not m.can_view():
|
||||
if not m.can_view_slow():
|
||||
abort(403, 'forbidden')
|
||||
return m
|
||||
except (NotFound, ValueError):
|
||||
@@ -324,14 +344,23 @@ class VCount(Validator):
|
||||
def run(self, count):
|
||||
if count is None:
|
||||
count = 0
|
||||
return max(int(count), 0)
|
||||
try:
|
||||
return max(int(count), 0)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
|
||||
class VLimit(Validator):
|
||||
def run(self, limit):
|
||||
if limit is None:
|
||||
return c.user.pref_numsites
|
||||
return min(max(int(limit), 1), 100)
|
||||
return c.user.pref_numsites
|
||||
|
||||
try:
|
||||
i = int(limit)
|
||||
except ValueError:
|
||||
return c.user.pref_numsites
|
||||
|
||||
return min(max(i, 1), 100)
|
||||
|
||||
class VCssMeasure(Validator):
|
||||
measure = re.compile(r"^\s*[\d\.]+\w{0,3}\s*$")
|
||||
@@ -371,23 +400,47 @@ class VLength(Validator):
|
||||
self.set_error(self.length_error, {'max_length': self.max_length})
|
||||
else:
|
||||
return text
|
||||
|
||||
|
||||
class VPrintable(VLength):
|
||||
def run(self, text, text2 = ''):
|
||||
text = VLength.run(self, text, text2)
|
||||
|
||||
if text is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
if all(isprint(str(x)) for x in text):
|
||||
return str(text)
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
|
||||
self.set_error(errors.BAD_STRING)
|
||||
return None
|
||||
|
||||
|
||||
class VTitle(VLength):
|
||||
def __init__(self, param, max_length = 300, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
class VComment(VLength):
|
||||
def __init__(self, param, max_length = 10000, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
class VSelfText(VLength):
|
||||
def __init__(self, param, max_length = 10000, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
class VMessage(VLength):
|
||||
|
||||
class VMarkdown(VLength):
|
||||
def __init__(self, param, max_length = 10000, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
def run(self, text, text2 = ''):
|
||||
text = text or text2
|
||||
VLength.run(self, text)
|
||||
try:
|
||||
markdown_souptest(text)
|
||||
return text
|
||||
except ValueError:
|
||||
import sys
|
||||
user = "???"
|
||||
if c.user_is_loggedin:
|
||||
user = c.user.name
|
||||
g.log.error("HAX by %s: %s" % (user, text))
|
||||
s = sys.exc_info()
|
||||
# reraise the original error with the original stack trace
|
||||
raise s[1], None, s[2]
|
||||
|
||||
class VSubredditName(VRequired):
|
||||
def __init__(self, item, *a, **kw):
|
||||
@@ -422,7 +475,7 @@ class VSubredditDesc(Validator):
|
||||
class VAccountByName(VRequired):
|
||||
def __init__(self, param, error = errors.USER_DOESNT_EXIST, *a, **kw):
|
||||
VRequired.__init__(self, param, error, *a, **kw)
|
||||
|
||||
|
||||
def run(self, name):
|
||||
if name:
|
||||
try:
|
||||
@@ -486,7 +539,7 @@ class VUser(Validator):
|
||||
|
||||
if (password is not None) and not valid_password(c.user, password):
|
||||
self.set_error(errors.WRONG_PASSWORD)
|
||||
|
||||
|
||||
class VModhash(Validator):
|
||||
default_param = 'uh'
|
||||
def run(self, uh):
|
||||
@@ -595,7 +648,10 @@ class VSubmitParent(VByName):
|
||||
if fullname:
|
||||
parent = VByName.run(self, fullname)
|
||||
if parent and parent._deleted:
|
||||
self.set_error(errors.DELETED_COMMENT)
|
||||
if isinstance(parent, Link):
|
||||
self.set_error(errors.DELETED_LINK)
|
||||
else:
|
||||
self.set_error(errors.DELETED_COMMENT)
|
||||
if isinstance(parent, Message):
|
||||
return parent
|
||||
else:
|
||||
@@ -623,7 +679,7 @@ class VSubmitSR(Validator):
|
||||
self.set_error(errors.SUBREDDIT_NOTALLOWED)
|
||||
else:
|
||||
return sr
|
||||
|
||||
|
||||
pass_rx = re.compile(r"^.{3,20}$")
|
||||
|
||||
def chkpass(x):
|
||||
@@ -633,12 +689,10 @@ class VPassword(Validator):
|
||||
def run(self, password, verify):
|
||||
if not chkpass(password):
|
||||
self.set_error(errors.BAD_PASSWORD)
|
||||
return
|
||||
elif verify != password:
|
||||
self.set_error(errors.BAD_PASSWORD_MATCH)
|
||||
return password
|
||||
else:
|
||||
return password
|
||||
return password.encode('utf8')
|
||||
|
||||
user_rx = re.compile(r"^[\w-]{3,20}$", re.UNICODE)
|
||||
|
||||
@@ -667,11 +721,15 @@ class VUname(VRequired):
|
||||
class VLogin(VRequired):
|
||||
def __init__(self, item, *a, **kw):
|
||||
VRequired.__init__(self, item, errors.WRONG_PASSWORD, *a, **kw)
|
||||
|
||||
|
||||
def run(self, user_name, password):
|
||||
user_name = chkuser(user_name)
|
||||
user = None
|
||||
if user_name:
|
||||
try:
|
||||
str(password)
|
||||
except UnicodeEncodeError:
|
||||
password = password.encode('utf8')
|
||||
user = valid_login(user_name, password)
|
||||
if not user:
|
||||
return self.error()
|
||||
@@ -698,7 +756,7 @@ class VUrl(VRequired):
|
||||
sr = None
|
||||
else:
|
||||
sr = None
|
||||
|
||||
|
||||
if not url:
|
||||
return self.error(errors.NO_URL)
|
||||
url = utils.sanitize_url(url)
|
||||
@@ -736,6 +794,21 @@ class VExistingUname(VRequired):
|
||||
return self.error(errors.USER_DOESNT_EXIST)
|
||||
self.error()
|
||||
|
||||
class VMessageRecipent(VExistingUname):
|
||||
def run(self, name):
|
||||
if not name:
|
||||
return self.error()
|
||||
if name.startswith('#'):
|
||||
try:
|
||||
s = Subreddit._by_name(name.strip('#'))
|
||||
if isinstance(s, FakeSubreddit):
|
||||
raise NotFound, "fake subreddit"
|
||||
return s
|
||||
except NotFound:
|
||||
self.set_error(errors.SUBREDDIT_NOEXIST)
|
||||
else:
|
||||
return VExistingUname.run(self, name)
|
||||
|
||||
class VUserWithEmail(VExistingUname):
|
||||
def run(self, name):
|
||||
user = VExistingUname.run(self, name)
|
||||
@@ -901,9 +974,11 @@ class VRatelimit(Validator):
|
||||
class VCommentIDs(Validator):
|
||||
#id_str is a comma separated list of id36's
|
||||
def run(self, id_str):
|
||||
cids = [int(i, 36) for i in id_str.split(',')]
|
||||
comments = Comment._byID(cids, data=True, return_dict = False)
|
||||
return comments
|
||||
if id_str:
|
||||
cids = [int(i, 36) for i in id_str.split(',')]
|
||||
comments = Comment._byID(cids, data=True, return_dict = False)
|
||||
return comments
|
||||
return []
|
||||
|
||||
|
||||
class CachedUser(object):
|
||||
@@ -1049,14 +1124,9 @@ class ValidIP(Validator):
|
||||
self.set_error(errors.BANNED_IP)
|
||||
return request.ip
|
||||
|
||||
class ValidDomain(Validator):
|
||||
class VOkayDomain(Validator):
|
||||
def run(self, url):
|
||||
if url and is_banned_domain(url):
|
||||
self.set_error(errors.BANNED_DOMAIN)
|
||||
|
||||
|
||||
|
||||
|
||||
return is_banned_domain(url)
|
||||
|
||||
class VDate(Validator):
|
||||
"""
|
||||
@@ -1135,9 +1205,32 @@ class VDestination(Validator):
|
||||
def __init__(self, param = 'dest', default = "", **kw):
|
||||
self.default = default
|
||||
Validator.__init__(self, param, **kw)
|
||||
|
||||
|
||||
def run(self, dest):
|
||||
return dest or request.referer or self.default
|
||||
if not dest:
|
||||
dest = request.referer or self.default or "/"
|
||||
|
||||
ld = dest.lower()
|
||||
if (ld.startswith("/") or
|
||||
ld.startswith("http://") or
|
||||
ld.startswith("https://")):
|
||||
|
||||
u = UrlParser(dest)
|
||||
|
||||
if u.is_reddit_url():
|
||||
return dest
|
||||
|
||||
ip = getattr(request, "ip", "[unknown]")
|
||||
fp = getattr(request, "fullpath", "[unknown]")
|
||||
dm = c.domain or "[unknown]"
|
||||
cn = c.cname or "[unknown]"
|
||||
|
||||
log_text("invalid redirect",
|
||||
"%s attempted to redirect from %s to %s with domain %s and cname %s"
|
||||
% (ip, fp, dest, dm, cn),
|
||||
"info")
|
||||
|
||||
return "/"
|
||||
|
||||
class ValidAddress(Validator):
|
||||
def __init__(self, param, usa_only = True):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,10 +29,10 @@ import time
|
||||
import errno
|
||||
import socket
|
||||
import itertools
|
||||
import pickle
|
||||
|
||||
from amqplib import client_0_8 as amqp
|
||||
|
||||
from r2.lib.cache import LocalCache
|
||||
from pylons import g
|
||||
|
||||
amqp_host = g.amqp_host
|
||||
@@ -88,7 +88,7 @@ def get_connection():
|
||||
virtual_host = amqp_virtual_host,
|
||||
insist = False)
|
||||
except (socket.error, IOError):
|
||||
print 'error connecting to amqp'
|
||||
print 'error connecting to amqp %s @ %s' % (amqp_user, amqp_host)
|
||||
time.sleep(1)
|
||||
|
||||
# don't run init_queue until someone actually needs it. this
|
||||
@@ -160,26 +160,42 @@ def add_item(routing_key, body, message_id = None):
|
||||
|
||||
worker.do(_add_item, routing_key, body, message_id = message_id)
|
||||
|
||||
def handle_items(queue, callback, ack = True, limit = 1, drain = False):
|
||||
def add_kw(routing_key, **kw):
|
||||
add_item(routing_key, pickle.dumps(kw))
|
||||
|
||||
def handle_items(queue, callback, ack = True, limit = 1, drain = False,
|
||||
verbose=True, sleep_time = 1):
|
||||
"""Call callback() on every item in a particular queue. If the
|
||||
connection to the queue is lost, it will die. Intended to be
|
||||
used as a long-running process."""
|
||||
|
||||
chan = get_channel()
|
||||
countdown = None
|
||||
|
||||
while True:
|
||||
|
||||
# NB: None != 0, so we don't need an "is not None" check here
|
||||
if countdown == 0:
|
||||
break
|
||||
|
||||
msg = chan.basic_get(queue)
|
||||
if not msg and drain:
|
||||
return
|
||||
elif not msg:
|
||||
time.sleep(1)
|
||||
time.sleep(sleep_time)
|
||||
continue
|
||||
|
||||
if countdown is None and drain and 'message_count' in msg.delivery_info:
|
||||
countdown = 1 + msg.delivery_info['message_count']
|
||||
|
||||
g.reset_caches()
|
||||
|
||||
items = []
|
||||
|
||||
while msg:
|
||||
while msg and countdown != 0:
|
||||
items.append(msg)
|
||||
if countdown is not None:
|
||||
countdown -= 1
|
||||
if len(items) >= limit:
|
||||
break # the innermost loop only
|
||||
msg = chan.basic_get(queue)
|
||||
@@ -190,7 +206,8 @@ def handle_items(queue, callback, ack = True, limit = 1, drain = False):
|
||||
# the count from the last message, if the count is
|
||||
# available
|
||||
count_str = '(%d remaining)' % items[-1].delivery_info['message_count']
|
||||
print "%s: %d items %s" % (queue, len(items), count_str)
|
||||
if verbose:
|
||||
print "%s: %d items %s" % (queue, len(items), count_str)
|
||||
callback(items, chan)
|
||||
|
||||
if ack:
|
||||
@@ -205,6 +222,7 @@ def handle_items(queue, callback, ack = True, limit = 1, drain = False):
|
||||
chan.basic_reject(item.delivery_tag, requeue = True)
|
||||
raise
|
||||
|
||||
|
||||
def empty_queue(queue):
|
||||
"""debug function to completely erase the contents of a queue"""
|
||||
chan = get_channel()
|
||||
|
||||
@@ -23,8 +23,9 @@ from __future__ import with_statement
|
||||
from pylons import config
|
||||
import pytz, os, logging, sys, socket, re, subprocess
|
||||
from datetime import timedelta, datetime
|
||||
from r2.lib.cache import LocalCache, Memcache, HardCache, CacheChain
|
||||
from r2.lib.cache import SelfEmptyingCache
|
||||
from r2.lib.cache import LocalCache, SelfEmptyingCache
|
||||
from r2.lib.cache import Memcache, Permacache, HardCache
|
||||
from r2.lib.cache import MemcacheChain, DoubleMemcacheChain, PermacacheChain, HardcacheChain
|
||||
from r2.lib.db.stats import QueryStats
|
||||
from r2.lib.translation import get_active_langs
|
||||
from r2.lib.lock import make_lock_factory
|
||||
@@ -57,7 +58,7 @@ class Globals(object):
|
||||
]
|
||||
|
||||
bool_props = ['debug', 'translator',
|
||||
'log_start',
|
||||
'log_start',
|
||||
'sqlprinting',
|
||||
'template_debug',
|
||||
'uncompressedJS',
|
||||
@@ -67,7 +68,9 @@ class Globals(object):
|
||||
'css_killswitch',
|
||||
'db_create_tables',
|
||||
'disallow_db_writes',
|
||||
'allow_shutdown']
|
||||
'exception_logging',
|
||||
'enable_usage_stats',
|
||||
]
|
||||
|
||||
tuple_props = ['memcaches',
|
||||
'rec_cache',
|
||||
@@ -77,9 +80,9 @@ class Globals(object):
|
||||
'sponsors',
|
||||
'monitored_servers',
|
||||
'automatic_reddits',
|
||||
'skip_precompute_queries',
|
||||
'agents',
|
||||
'allowed_css_linked_domains']
|
||||
'allowed_css_linked_domains',
|
||||
'authorized_cnames']
|
||||
|
||||
def __init__(self, global_conf, app_conf, paths, **extra):
|
||||
"""
|
||||
@@ -89,22 +92,22 @@ class Globals(object):
|
||||
One instance of Globals is created by Pylons during
|
||||
application initialization and is available during requests
|
||||
via the 'g' variable.
|
||||
|
||||
|
||||
``global_conf``
|
||||
The same variable used throughout ``config/middleware.py``
|
||||
namely, the variables from the ``[DEFAULT]`` section of the
|
||||
configuration file.
|
||||
|
||||
|
||||
``app_conf``
|
||||
The same ``kw`` dictionary used throughout
|
||||
``config/middleware.py`` namely, the variables from the
|
||||
section in the config file for your application.
|
||||
|
||||
|
||||
``extra``
|
||||
The configuration returned from ``load_config`` in
|
||||
``config/middleware.py`` which may be of use in the setup of
|
||||
your global variables.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
# slop over all variables to start with
|
||||
@@ -122,21 +125,30 @@ class Globals(object):
|
||||
|
||||
self.running_as_script = global_conf.get('running_as_script', False)
|
||||
|
||||
self.skip_precompute_queries = set(self.skip_precompute_queries)
|
||||
|
||||
# initialize caches. Any cache-chains built here must be added
|
||||
# to reset_caches so that they can properly reset their local
|
||||
# components
|
||||
mc = Memcache(self.memcaches, pickleProtocol = 1)
|
||||
# to cache_chains (closed around by reset_caches) so that they
|
||||
# can properly reset their local components
|
||||
|
||||
localcache_cls = SelfEmptyingCache if self.running_as_script else LocalCache
|
||||
|
||||
# we're going to temporarily run the old memcached behind the
|
||||
# new one so the caches can start warmer
|
||||
# mc = Memcache(self.memcaches, debug=self.debug)
|
||||
mc = Permacache(self.memcaches)
|
||||
rec_cache = Permacache(self.rec_cache)
|
||||
rmc = Permacache(self.rendercaches)
|
||||
pmc = Permacache(self.permacaches)
|
||||
# hardcache is done after the db info is loaded, and then the
|
||||
# chains are reset to use the appropriate initial entries
|
||||
self.memcache = mc
|
||||
self.cache = CacheChain((LocalCache(), mc))
|
||||
self.permacache = Memcache(self.permacaches, pickleProtocol = 1)
|
||||
self.rendercache = Memcache(self.rendercaches, pickleProtocol = 1)
|
||||
self.cache = PermacacheChain((localcache_cls(), mc))
|
||||
self.permacache = PermacacheChain((localcache_cls(), pmc))
|
||||
self.rendercache = PermacacheChain((localcache_cls(), rmc))
|
||||
self.rec_cache = rec_cache
|
||||
|
||||
self.make_lock = make_lock_factory(mc)
|
||||
cache_chains = [self.cache, self.permacache, self.rendercache]
|
||||
|
||||
self.rec_cache = Memcache(self.rec_cache, pickleProtocol = 1)
|
||||
|
||||
# set default time zone if one is not set
|
||||
tz = global_conf.get('timezone')
|
||||
dtz = global_conf.get('display_timezone', tz)
|
||||
@@ -148,9 +160,19 @@ class Globals(object):
|
||||
self.dbm = self.load_db_params(global_conf)
|
||||
|
||||
# can't do this until load_db_params() has been called
|
||||
self.hardcache = CacheChain((LocalCache(), mc, HardCache(self)),
|
||||
cache_negative_results = True)
|
||||
self.hardcache = HardcacheChain((localcache_cls(), mc, HardCache(self)),
|
||||
cache_negative_results = True)
|
||||
cache_chains.append(self.hardcache)
|
||||
|
||||
# I know this sucks, but we need non-request-threads to be
|
||||
# able to reset the caches, so we need them be able to close
|
||||
# around 'cache_chains' without being able to call getattr on
|
||||
# 'g'
|
||||
def reset_caches():
|
||||
for chain in cache_chains:
|
||||
chain.reset()
|
||||
|
||||
self.reset_caches = reset_caches
|
||||
self.reset_caches()
|
||||
|
||||
#make a query cache
|
||||
@@ -169,6 +191,8 @@ class Globals(object):
|
||||
all_languages.sort()
|
||||
self.all_languages = all_languages
|
||||
|
||||
self.paths = paths
|
||||
|
||||
# load the md5 hashes of files under static
|
||||
static_files = os.path.join(paths.get('static_files'), 'static')
|
||||
self.static_md5 = {}
|
||||
@@ -185,6 +209,7 @@ class Globals(object):
|
||||
#set up the logging directory
|
||||
log_path = self.log_path
|
||||
process_iden = global_conf.get('scgi_port', 'default')
|
||||
self.reddit_port = process_iden
|
||||
if log_path:
|
||||
if not os.path.exists(log_path):
|
||||
os.makedirs(log_path)
|
||||
@@ -207,7 +232,8 @@ class Globals(object):
|
||||
if not self.media_domain:
|
||||
self.media_domain = self.domain
|
||||
if self.media_domain == self.domain:
|
||||
print "Warning: g.media_domain == g.domain. This may give untrusted content access to user cookies"
|
||||
print ("Warning: g.media_domain == g.domain. " +
|
||||
"This may give untrusted content access to user cookies")
|
||||
|
||||
#read in our CSS so that it can become a default for subreddit
|
||||
#stylesheets
|
||||
@@ -253,12 +279,6 @@ class Globals(object):
|
||||
if self.log_start:
|
||||
self.log.error("reddit app started %s at %s" % (self.short_version, datetime.now()))
|
||||
|
||||
def reset_caches(self):
|
||||
for ca in ('cache', 'hardcache'):
|
||||
cache = getattr(self, ca)
|
||||
new_cache = SelfEmptyingCache() if self.running_as_script else LocalCache()
|
||||
cache.caches = (new_cache,) + cache.caches[1:]
|
||||
|
||||
@staticmethod
|
||||
def to_bool(x):
|
||||
return (x.lower() == 'true') if x else None
|
||||
|
||||
@@ -28,10 +28,12 @@ from r2.lib.utils import to_js
|
||||
from r2.lib.filters import spaceCompress, _force_unicode
|
||||
from r2.lib.template_helpers import get_domain
|
||||
from utils import storify, string2js, read_http_date
|
||||
from r2.lib.log import log_exception
|
||||
|
||||
import re, md5
|
||||
from urllib import quote
|
||||
from urllib import quote
|
||||
import urllib2
|
||||
import sys
|
||||
|
||||
|
||||
#TODO hack
|
||||
@@ -40,18 +42,22 @@ from r2.lib.utils import UrlParser, query_string
|
||||
logging.getLogger('scgi-wsgi').setLevel(logging.CRITICAL)
|
||||
|
||||
class BaseController(WSGIController):
|
||||
def __after__(self):
|
||||
self.post()
|
||||
def try_pagecache(self):
|
||||
pass
|
||||
|
||||
def __before__(self):
|
||||
self.pre()
|
||||
self.try_pagecache()
|
||||
|
||||
def __after__(self):
|
||||
self.post()
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
true_client_ip = environ.get('HTTP_TRUE_CLIENT_IP')
|
||||
ip_hash = environ.get('HTTP_TRUE_CLIENT_IP_HASH')
|
||||
forwarded_for = environ.get('HTTP_X_FORWARDED_FOR', ())
|
||||
remote_addr = environ.get('REMOTE_ADDR')
|
||||
|
||||
|
||||
if (g.ip_hash
|
||||
and true_client_ip
|
||||
and ip_hash
|
||||
@@ -93,9 +99,18 @@ class BaseController(WSGIController):
|
||||
c.thread_pool = environ['paste.httpserver.thread_pool']
|
||||
|
||||
c.response = Response()
|
||||
res = WSGIController.__call__(self, environ, start_response)
|
||||
try:
|
||||
res = WSGIController.__call__(self, environ, start_response)
|
||||
except Exception as e:
|
||||
if g.exception_logging:
|
||||
try:
|
||||
log_exception(e, *sys.exc_info())
|
||||
except Exception as f:
|
||||
print "log_exception() freaked out: %r" % f
|
||||
print "sorry for breaking the stack trace:"
|
||||
raise
|
||||
return res
|
||||
|
||||
|
||||
def pre(self): pass
|
||||
def post(self): pass
|
||||
|
||||
@@ -154,7 +169,7 @@ class BaseController(WSGIController):
|
||||
Reformats the new Location (dest) using format_output_url and
|
||||
sends the user to that location with the provided HTTP code.
|
||||
"""
|
||||
dest = cls.format_output_url(dest)
|
||||
dest = cls.format_output_url(dest or "/")
|
||||
c.response.headers['Location'] = dest
|
||||
c.response.status_code = code
|
||||
return c.response
|
||||
|
||||
84
r2/r2/lib/c/reddit-discount-wrapper.c
Normal file
84
r2/r2/lib/c/reddit-discount-wrapper.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mkdio.h"
|
||||
|
||||
typedef struct rd_opts_s {
|
||||
const char * target;
|
||||
int nofollow;
|
||||
} rd_opts_t;
|
||||
|
||||
char *
|
||||
cb_flagmaker (const char * text, const int size, void * arg)
|
||||
{
|
||||
rd_opts_t * opts;
|
||||
char * rv;
|
||||
int rv_size;
|
||||
int bytes_written;
|
||||
|
||||
#define TARGET_TAG "target="
|
||||
#define NOFOLLOW " rel='nofollow'"
|
||||
|
||||
opts = (rd_opts_t *) arg;
|
||||
|
||||
if (opts->target == NULL) {
|
||||
opts->target = "";
|
||||
}
|
||||
|
||||
if (opts->target[0] == '\0') {
|
||||
rv_size = 1; /* need room for a \0 */
|
||||
} else {
|
||||
/* Need to add 2 more, for the surrounding quotes */
|
||||
rv_size = sizeof(TARGET_TAG) + strlen(opts->target) + 2;
|
||||
}
|
||||
|
||||
if (opts->nofollow) {
|
||||
/* We can subtract 1 because the \0 is already accounted for */
|
||||
rv_size += sizeof(NOFOLLOW) - 1;
|
||||
}
|
||||
|
||||
rv = malloc(rv_size);
|
||||
|
||||
bytes_written = 1 + sprintf (rv, "%s%s%s%s%s",
|
||||
opts->target[0] == '\0' ? "" : TARGET_TAG,
|
||||
opts->target[0] == '\0' ? "" : "'",
|
||||
opts->target,
|
||||
opts->target[0] == '\0' ? "" : "'",
|
||||
opts->nofollow ? NOFOLLOW : "");
|
||||
|
||||
if (bytes_written > rv_size) {
|
||||
fprintf (stderr, "Augh, allocated %d bytes and wrote %d bytes\n",
|
||||
rv_size, bytes_written);
|
||||
abort();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
reddit_discount_wrap(const char * text, int nofollow, const char * target,
|
||||
void ** v_mmiot, char ** html, int * size)
|
||||
{
|
||||
rd_opts_t opts;
|
||||
MMIOT * mmiot;
|
||||
|
||||
opts.target = target;
|
||||
opts.nofollow = nofollow;
|
||||
|
||||
mmiot = mkd_string((char *) text, strlen(text), 0);
|
||||
|
||||
mkd_compile(mmiot, MKD_NOHTML | MKD_NOIMAGE | MKD_NOPANTS | MKD_NOHEADER |
|
||||
MKD_NO_EXT | MKD_AUTOLINK | MKD_SAFELINK);
|
||||
|
||||
mkd_e_flags (mmiot, &cb_flagmaker);
|
||||
mkd_e_context(mmiot, &opts);
|
||||
|
||||
*size = mkd_document(mmiot, html);
|
||||
*v_mmiot = mmiot;
|
||||
}
|
||||
|
||||
void
|
||||
reddit_discount_cleanup (void * v_mmiot) {
|
||||
mkd_cleanup(v_mmiot);
|
||||
}
|
||||
|
||||
@@ -1,2 +1,19 @@
|
||||
from ctypes import cdll, c_int, c_void_p, byref, string_at
|
||||
from r2.lib.filters import _force_utf8
|
||||
from pylons import g
|
||||
|
||||
libmd = cdll.LoadLibrary(g.paths['root'] + '/../reddit-discount.so')
|
||||
|
||||
def c_markdown(text, nofollow=False, target=None):
|
||||
raise NotImplementedError()
|
||||
u8 = _force_utf8(text)
|
||||
size = c_int(len(u8))
|
||||
nofollow = 1 if nofollow else 0
|
||||
doc = c_void_p()
|
||||
html = c_void_p()
|
||||
|
||||
libmd.reddit_discount_wrap(u8, nofollow, target,
|
||||
byref(doc), byref(html), byref(size))
|
||||
r = string_at(html, size)
|
||||
libmd.reddit_discount_cleanup(doc)
|
||||
|
||||
return r
|
||||
|
||||
@@ -20,19 +20,22 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from threading import local
|
||||
from hashlib import md5
|
||||
|
||||
from utils import lstrips, in_chunks
|
||||
import pylibmc
|
||||
from _pylibmc import MemcachedError
|
||||
from contrib import memcache
|
||||
|
||||
from utils import lstrips, in_chunks, tup
|
||||
from r2.lib.hardcachebackend import HardCacheBackend
|
||||
|
||||
class NoneResult(object): pass
|
||||
|
||||
class CacheUtils(object):
|
||||
def incr_multi(self, keys, delta=1, time=0, prefix=''):
|
||||
def incr_multi(self, keys, delta=1, prefix=''):
|
||||
for k in keys:
|
||||
try:
|
||||
self.incr(prefix + k, time=time, delta=delta)
|
||||
self.incr(prefix + k, delta)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -53,9 +56,14 @@ class CacheUtils(object):
|
||||
|
||||
return dict((key_map[k], r[k]) for k in r.keys())
|
||||
|
||||
class Memcache(CacheUtils, memcache.Client):
|
||||
class Permacache(CacheUtils, memcache.Client):
|
||||
"""We still use our patched python-memcache to talk to the
|
||||
permacaches for legacy reasons"""
|
||||
simple_get_multi = memcache.Client.get_multi
|
||||
|
||||
def __init__(self, servers):
|
||||
memcache.Client.__init__(self, servers, pickleProtocol = 1)
|
||||
|
||||
def set_multi(self, keys, prefix='', time=0):
|
||||
|
||||
new_keys = {}
|
||||
@@ -79,6 +87,62 @@ class Memcache(CacheUtils, memcache.Client):
|
||||
memcache.Client.delete_multi(self, keys, time = time,
|
||||
key_prefix = prefix)
|
||||
|
||||
def get_local_client(self):
|
||||
return self # memcache.py handles this itself
|
||||
|
||||
class Memcache(CacheUtils, pylibmc.Client):
|
||||
simple_get_multi = pylibmc.Client.get_multi
|
||||
|
||||
def __init__(self, servers,
|
||||
debug = False,
|
||||
binary=True,
|
||||
noreply=False):
|
||||
pylibmc.Client.__init__(self, servers, binary=binary)
|
||||
behaviors = {'no_block': True, # use async I/O
|
||||
'cache_lookups': True, # cache DNS lookups
|
||||
'tcp_nodelay': True, # no nagle
|
||||
'ketama': True, # consistant hashing
|
||||
'_noreply': int(noreply),
|
||||
'verify_key': int(debug)} # spend the CPU to verify keys
|
||||
self.behaviors.update(behaviors)
|
||||
self.local_clients = local()
|
||||
|
||||
def get_local_client(self):
|
||||
# if this thread hasn't had one yet, make one
|
||||
if not getattr(self.local_clients, 'client', None):
|
||||
self.local_clients.client = self.clone()
|
||||
return self.local_clients.client
|
||||
|
||||
def set_multi(self, keys, prefix='', time=0):
|
||||
new_keys = {}
|
||||
for k,v in keys.iteritems():
|
||||
new_keys[str(k)] = v
|
||||
pylibmc.Client.set_multi(self, new_keys, key_prefix = prefix,
|
||||
time = time)
|
||||
|
||||
def incr(self, key, delta=1, time=0):
|
||||
# ignore the time on these
|
||||
return pylibmc.Client.incr(self, key, delta)
|
||||
|
||||
def add(self, key, val, time=0):
|
||||
try:
|
||||
return pylibmc.Client.add(self, key, val, time=time)
|
||||
except pylibmc.DataExists:
|
||||
return None
|
||||
|
||||
def get(self, key, default=None):
|
||||
r = pylibmc.Client.get(self, key)
|
||||
if r is None:
|
||||
return default
|
||||
return r
|
||||
|
||||
def set(self, key, val, time=0):
|
||||
pylibmc.Client.set(self, key, val, time = time)
|
||||
|
||||
def delete_multi(self, keys, prefix='', time=0):
|
||||
pylibmc.Client.delete_multi(self, keys, time = time,
|
||||
key_prefix = prefix)
|
||||
|
||||
class HardCache(CacheUtils):
|
||||
backend = None
|
||||
|
||||
@@ -161,7 +225,6 @@ class LocalCache(dict, CacheUtils):
|
||||
for k in keys:
|
||||
if self.has_key(k):
|
||||
out[k] = self[k]
|
||||
# print "Local cache answers: " + str(out)
|
||||
return out
|
||||
|
||||
def set(self, key, val, time = 0):
|
||||
@@ -175,7 +238,9 @@ class LocalCache(dict, CacheUtils):
|
||||
|
||||
def add(self, key, val, time = 0):
|
||||
self._check_key(key)
|
||||
return self.setdefault(key, val)
|
||||
was = key in self
|
||||
self.setdefault(key, val)
|
||||
return not was
|
||||
|
||||
def delete(self, key):
|
||||
if self.has_key(key):
|
||||
@@ -221,6 +286,12 @@ class CacheChain(CacheUtils, local):
|
||||
return ret
|
||||
return fn
|
||||
|
||||
# note that because of the naive nature of `add' when used on a
|
||||
# cache chain, its return value isn't reliable. if you need to
|
||||
# verify its return value you'll either need to make it smarter or
|
||||
# use the underlying cache directly
|
||||
add = make_set_fn('add')
|
||||
|
||||
set = make_set_fn('set')
|
||||
append = make_set_fn('append')
|
||||
prepend = make_set_fn('prepend')
|
||||
@@ -234,33 +305,6 @@ class CacheChain(CacheUtils, local):
|
||||
flush_all = make_set_fn('flush_all')
|
||||
cache_negative_results = False
|
||||
|
||||
def add(self, key, val, time=0):
|
||||
authority = self.caches[-1]
|
||||
added_val = authority.add(key, val, time=time)
|
||||
for cache in self.caches[:-1]:
|
||||
# Calling set() rather than add() to ensure that all caches are
|
||||
# in sync and that de-syncs repair themselves
|
||||
cache.set(key, added_val, time=time)
|
||||
return added_val
|
||||
|
||||
def accrue(self, key, time=0, delta=1):
|
||||
auth_value = self.caches[-1].get(key)
|
||||
|
||||
if auth_value is None:
|
||||
self.caches[-1].set(key, 0, time)
|
||||
auth_value = 0
|
||||
|
||||
try:
|
||||
auth_value = int(auth_value)
|
||||
except ValueError:
|
||||
raise ValueError("Can't accrue %s; it's a %s (%r)" %
|
||||
(key, auth_value.__class__.__name__, auth_value))
|
||||
|
||||
for c in self.caches:
|
||||
c.set(key, auth_value, time=time)
|
||||
|
||||
self.incr(key, time=time, delta=delta)
|
||||
|
||||
def get(self, key, default = None, local = True):
|
||||
for c in self.caches:
|
||||
if not local and isinstance(c,LocalCache):
|
||||
@@ -271,12 +315,12 @@ class CacheChain(CacheUtils, local):
|
||||
if val is not None:
|
||||
#update other caches
|
||||
for d in self.caches:
|
||||
if c == d:
|
||||
if c is d:
|
||||
break # so we don't set caches later in the chain
|
||||
d.set(key, val)
|
||||
|
||||
if self.cache_negative_results and val is NoneResult:
|
||||
return None
|
||||
return default
|
||||
else:
|
||||
return val
|
||||
|
||||
@@ -293,12 +337,13 @@ class CacheChain(CacheUtils, local):
|
||||
need = set(keys)
|
||||
for c in self.caches:
|
||||
if len(out) == len(keys):
|
||||
# we've found them all
|
||||
break
|
||||
r = c.simple_get_multi(need)
|
||||
#update other caches
|
||||
if r:
|
||||
for d in self.caches:
|
||||
if c == d:
|
||||
if c is d:
|
||||
break # so we don't set caches later in the chain
|
||||
d.set_multi(r)
|
||||
r.update(out)
|
||||
@@ -319,12 +364,89 @@ class CacheChain(CacheUtils, local):
|
||||
|
||||
return out
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s>' % (self.__class__.__name__,)
|
||||
|
||||
def debug(self, key):
|
||||
print "Looking up [%r]" % key
|
||||
for i, c in enumerate(self.caches):
|
||||
print "[%d] %10s has value [%r]" % (i, c.__class__.__name__,
|
||||
c.get(key))
|
||||
|
||||
def reset(self):
|
||||
# the first item in a cache chain is a LocalCache
|
||||
self.caches = (self.caches[0].__class__(),) + self.caches[1:]
|
||||
|
||||
class MemcacheChain(CacheChain):
|
||||
def __init__(self, caches):
|
||||
CacheChain.__init__(self, caches)
|
||||
self.mc_master = self.caches[-1]
|
||||
|
||||
def reset(self):
|
||||
CacheChain.reset(self)
|
||||
localcache, old_mc = self.caches
|
||||
self.caches = (localcache, self.mc_master.get_local_client())
|
||||
|
||||
class DoubleMemcacheChain(CacheChain):
|
||||
"""Temporary cache chain that places the new cache ahead of the
|
||||
old one for easier deployment"""
|
||||
def __init__(self, caches):
|
||||
self.caches = localcache, memcache, permacache = caches
|
||||
self.mc_master = memcache
|
||||
|
||||
def reset(self):
|
||||
CacheChain.reset(self)
|
||||
self.caches = (self.caches[0],
|
||||
self.mc_master.get_local_client(),
|
||||
self.caches[2])
|
||||
|
||||
class PermacacheChain(CacheChain):
|
||||
pass
|
||||
|
||||
class HardcacheChain(CacheChain):
|
||||
def __init__(self, caches, cache_negative_results = False):
|
||||
CacheChain.__init__(self, caches, cache_negative_results)
|
||||
localcache, memcache, hardcache = self.caches
|
||||
self.mc_master = memcache
|
||||
|
||||
def add(self, key, val, time=0):
|
||||
authority = self.caches[-1] # the authority is the hardcache
|
||||
# itself
|
||||
added_val = authority.add(key, val, time=time)
|
||||
for cache in self.caches[:-1]:
|
||||
# Calling set() rather than add() to ensure that all caches are
|
||||
# in sync and that de-syncs repair themselves
|
||||
cache.set(key, added_val, time=time)
|
||||
return added_val
|
||||
|
||||
def accrue(self, key, time=0, delta=1):
|
||||
auth_value = self.caches[-1].get(key)
|
||||
|
||||
if auth_value is None:
|
||||
self.caches[-1].set(key, 0, time)
|
||||
auth_value = 0
|
||||
|
||||
try:
|
||||
auth_value = int(auth_value) + delta
|
||||
except ValueError:
|
||||
raise ValueError("Can't accrue %s; it's a %s (%r)" %
|
||||
(key, auth_value.__class__.__name__, auth_value))
|
||||
|
||||
for c in self.caches:
|
||||
c.set(key, auth_value, time=time)
|
||||
|
||||
@property
|
||||
def backend(self):
|
||||
# the hardcache is always the last item in a HardCacheChain
|
||||
return self.caches[-1].backend
|
||||
|
||||
def reset(self):
|
||||
CacheChain.reset(self)
|
||||
assert len(self.caches) == 3
|
||||
self.caches = (self.caches[0],
|
||||
self.mc_master.get_local_client(),
|
||||
self.caches[2])
|
||||
|
||||
#smart get multi
|
||||
def sgm(cache, keys, miss_fn, prefix='', time=0):
|
||||
keys = set(keys)
|
||||
@@ -340,34 +462,56 @@ def sgm(cache, keys, miss_fn, prefix='', time=0):
|
||||
|
||||
return dict((s_keys[k], v) for k,v in r.iteritems())
|
||||
|
||||
def test_cache(cache):
|
||||
def test_cache(cache, prefix=''):
|
||||
#basic set/get
|
||||
cache.set('1', 1)
|
||||
assert cache.get('1') == 1
|
||||
cache.set('%s1' % prefix, 1)
|
||||
assert cache.get('%s1' % prefix) == 1
|
||||
|
||||
#python data
|
||||
cache.set('2', [1,2,3])
|
||||
assert cache.get('2') == [1,2,3]
|
||||
cache.set('%s2' % prefix, [1,2,3])
|
||||
assert cache.get('%s2' % prefix) == [1,2,3]
|
||||
|
||||
#set multi, no prefix
|
||||
cache.set_multi({'3':3, '4': 4})
|
||||
assert cache.get_multi(('3', '4')) == {'3':3, '4': 4}
|
||||
cache.set_multi({'%s3' % prefix:3, '%s4' % prefix: 4})
|
||||
assert cache.get_multi(('%s3' % prefix, '%s4' % prefix)) == {'%s3' % prefix: 3,
|
||||
'%s4' % prefix: 4}
|
||||
|
||||
#set multi, prefix
|
||||
cache.set_multi({'3':3, '4': 4}, prefix='p_')
|
||||
assert cache.get_multi(('3', 4), prefix='p_') == {'3':3, 4: 4}
|
||||
assert cache.get_multi(('p_3', 'p_4')) == {'p_3':3, 'p_4': 4}
|
||||
cache.set_multi({'3':3, '4': 4}, prefix='%sp_' % prefix)
|
||||
assert cache.get_multi(('3', 4), prefix='%sp_' % prefix) == {'3':3, 4: 4}
|
||||
assert cache.get_multi(('%sp_3' % prefix, '%sp_4' % prefix)) == {'%sp_3'%prefix: 3,
|
||||
'%sp_4'%prefix: 4}
|
||||
|
||||
#incr
|
||||
cache.set('5', 1)
|
||||
cache.set('6', 1)
|
||||
cache.incr('5')
|
||||
assert cache.get('5') == 2
|
||||
cache.incr('5',2)
|
||||
assert cache.get('5') == 4
|
||||
cache.incr_multi(('5', '6'), 1)
|
||||
assert cache.get('5') == 5
|
||||
assert cache.get('6') == 2
|
||||
cache.set('%s5'%prefix, 1)
|
||||
cache.set('%s6'%prefix, 1)
|
||||
cache.incr('%s5'%prefix)
|
||||
assert cache.get('%s5'%prefix) == 2
|
||||
cache.incr('%s5'%prefix,2)
|
||||
assert cache.get('%s5'%prefix) == 4
|
||||
cache.incr_multi(('%s5'%prefix, '%s6'%prefix), 1)
|
||||
assert cache.get('%s5'%prefix) == 5
|
||||
assert cache.get('%s6'%prefix) == 2
|
||||
|
||||
def test_multi(cache):
|
||||
from threading import Thread
|
||||
|
||||
num_threads = 100
|
||||
num_per_thread = 1000
|
||||
|
||||
threads = []
|
||||
for x in range(num_threads):
|
||||
def _fn(prefix):
|
||||
def __fn():
|
||||
for y in range(num_per_thread):
|
||||
test_cache(cache,prefix=prefix)
|
||||
return __fn
|
||||
t = Thread(target=_fn(str(x)))
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
|
||||
# a cache that occasionally dumps itself to be used for long-running
|
||||
# processes
|
||||
@@ -379,9 +523,38 @@ class SelfEmptyingCache(LocalCache):
|
||||
if len(self) > self.max_size:
|
||||
self.clear()
|
||||
|
||||
def set(self,key,val,time = 0):
|
||||
def set(self, key, val, time=0):
|
||||
self.maybe_reset()
|
||||
return LocalCache.set(self,key,val,time)
|
||||
|
||||
def add(self,key,val):
|
||||
return self.set(key,val)
|
||||
def add(self, key, val, time=0):
|
||||
self.maybe_reset()
|
||||
return LocalCache.add(self, key, val)
|
||||
|
||||
def make_key(iden, *a, **kw):
|
||||
"""
|
||||
A helper function for making memcached-usable cache keys out of
|
||||
arbitrary arguments. Hashes the arguments but leaves the `iden'
|
||||
human-readable
|
||||
"""
|
||||
h = md5()
|
||||
|
||||
def _conv(s):
|
||||
if isinstance(s, str):
|
||||
return s
|
||||
elif isinstance(s, unicode):
|
||||
return s.encode('utf-8')
|
||||
elif isinstance(s, (tuple, list)):
|
||||
return ','.join(_conv(x) for x in s)
|
||||
elif isinstance(s, dict):
|
||||
return ','.join('%s:%s' % (_conv(k), _conv(v))
|
||||
for (k, v) in sorted(s.iteritems()))
|
||||
else:
|
||||
return str(s)
|
||||
|
||||
iden = _conv(iden)
|
||||
h.update(iden)
|
||||
h.update(_conv(a))
|
||||
h.update(_conv(kw))
|
||||
|
||||
return '%s(%s)' % (iden, h.hexdigest())
|
||||
|
||||
@@ -24,6 +24,8 @@ from pylons import g
|
||||
from Captcha.Base import randomIdentifier
|
||||
from Captcha.Visual import Text, Backgrounds, Distortions, ImageCaptcha
|
||||
|
||||
from r2.lib.cache import make_key
|
||||
|
||||
IDEN_LENGTH = 32
|
||||
SOL_LENGTH = 6
|
||||
|
||||
@@ -47,21 +49,24 @@ def make_solution():
|
||||
return randomIdentifier(alphabet=string.ascii_letters, length = SOL_LENGTH).upper()
|
||||
|
||||
def get_image(iden):
|
||||
solution = g.rendercache.get(str(iden))
|
||||
key = make_key(iden)
|
||||
solution = g.rendercache.get(key)
|
||||
if not solution:
|
||||
solution = make_solution()
|
||||
g.rendercache.set(str(iden), solution, time = 300)
|
||||
g.rendercache.set(key, solution, time = 300)
|
||||
return RandCaptcha(solution=solution).render()
|
||||
|
||||
def valid_solution(iden, solution):
|
||||
key = make_key(iden)
|
||||
|
||||
if (not iden
|
||||
or not solution
|
||||
or len(iden) != IDEN_LENGTH
|
||||
or len(solution) != SOL_LENGTH
|
||||
or solution.upper() != g.rendercache.get(str(iden))):
|
||||
or solution.upper() != g.rendercache.get(key)):
|
||||
solution = make_solution()
|
||||
g.rendercache.set(str(iden), solution, time = 300)
|
||||
g.rendercache.set(key, solution, time = 300)
|
||||
return False
|
||||
else:
|
||||
g.rendercache.delete(str(iden))
|
||||
g.rendercache.delete(key)
|
||||
return True
|
||||
|
||||
@@ -24,6 +24,7 @@ from __future__ import with_statement
|
||||
from pylons import g
|
||||
from itertools import chain
|
||||
from utils import tup
|
||||
from cache import sgm
|
||||
|
||||
def comments_key(link_id):
|
||||
return 'comments_' + str(link_id)
|
||||
@@ -86,10 +87,12 @@ def delete_comment(comment):
|
||||
#nothing really to do here, atm
|
||||
pass
|
||||
|
||||
def link_comments(link_id):
|
||||
def link_comments(link_id, _update=False):
|
||||
key = comments_key(link_id)
|
||||
|
||||
r = g.permacache.get(key)
|
||||
if r:
|
||||
|
||||
if r and not _update:
|
||||
return r
|
||||
else:
|
||||
with g.make_lock(lock_key(link_id)):
|
||||
@@ -148,14 +151,22 @@ def add_message(message):
|
||||
# add the message to the author's list and the recipient
|
||||
with g.make_lock(messages_lock_key(message.author_id)):
|
||||
add_message_nolock(message.author_id, message)
|
||||
with g.make_lock(messages_lock_key(message.to_id)):
|
||||
add_message_nolock(message.to_id, message)
|
||||
if message.to_id:
|
||||
with g.make_lock(messages_lock_key(message.to_id)):
|
||||
add_message_nolock(message.to_id, message)
|
||||
if message.sr_id:
|
||||
with g.make_lock(sr_messages_lock_key(message.sr_id)):
|
||||
add_sr_message_nolock(message.sr_id, message)
|
||||
|
||||
def add_message_nolock(user_id, message):
|
||||
|
||||
def _add_message_nolock(key, message):
|
||||
from r2.models import Account, Message
|
||||
key = messages_key(user_id)
|
||||
trees = g.permacache.get(key)
|
||||
if not trees:
|
||||
# in case an empty list got written at some point, delete it to
|
||||
# force a recompute
|
||||
if trees is not None:
|
||||
g.permacache.delete(key)
|
||||
# no point computing it now. We'll do it when they go to
|
||||
# their message page.
|
||||
return
|
||||
@@ -184,10 +195,11 @@ def add_message_nolock(user_id, message):
|
||||
g.permacache.set(key, trees)
|
||||
|
||||
|
||||
def conversation(user, parent):
|
||||
from r2.models import Message
|
||||
trees = dict(user_messages(user))
|
||||
def add_message_nolock(user_id, message):
|
||||
return _add_message_nolock(messages_key(user_id), message)
|
||||
|
||||
def _conversation(trees, parent):
|
||||
from r2.models import Message
|
||||
if parent._id in trees:
|
||||
convo = trees[parent._id]
|
||||
if convo:
|
||||
@@ -202,42 +214,95 @@ def conversation(user, parent):
|
||||
data = True)
|
||||
return compute_message_trees([parent] + list(m))
|
||||
|
||||
def user_messages(user):
|
||||
def conversation(user, parent):
|
||||
trees = dict(user_messages(user))
|
||||
return _conversation(trees, parent)
|
||||
|
||||
|
||||
def user_messages(user, update = False):
|
||||
key = messages_key(user._id)
|
||||
trees = g.permacache.get(key)
|
||||
if trees is None:
|
||||
if not trees or update:
|
||||
trees = user_messages_nocache(user)
|
||||
g.permacache.set(key, trees)
|
||||
return trees
|
||||
|
||||
def _process_message_query(inbox):
|
||||
if hasattr(inbox, 'prewrap_fn'):
|
||||
return [inbox.prewrap_fn(i) for i in inbox]
|
||||
return list(inbox)
|
||||
|
||||
|
||||
def _load_messages(mlist):
|
||||
from r2.models import Message
|
||||
m = {}
|
||||
ids = [x for x in mlist if not isinstance(x, Message)]
|
||||
if ids:
|
||||
m = Message._by_fullname(ids, return_dict = True, data = True)
|
||||
messages = [m.get(x, x) for x in mlist]
|
||||
return messages
|
||||
|
||||
def user_messages_nocache(user):
|
||||
"""
|
||||
Just like user_messages, but avoiding the cache
|
||||
"""
|
||||
from r2.lib.db import queries
|
||||
from r2.models import Message
|
||||
|
||||
inbox = queries.get_inbox_messages(user)
|
||||
if hasattr(inbox, 'prewrap_fn'):
|
||||
inbox = [inbox.prewrap_fn(i) for i in inbox]
|
||||
else:
|
||||
inbox = list(inbox)
|
||||
|
||||
sent = queries.get_sent(user)
|
||||
if hasattr(sent, 'prewrap_fn'):
|
||||
sent = [sent.prewrap_fn(i) for i in sent]
|
||||
else:
|
||||
sent = list(sent)
|
||||
|
||||
m = {}
|
||||
ids = [x for x in chain(inbox, sent) if not isinstance(x, Message)]
|
||||
if ids:
|
||||
m = Message._by_fullname(ids, return_dict = True, data = True)
|
||||
|
||||
messages = [m.get(x, x) for x in chain(inbox, sent)]
|
||||
|
||||
inbox = _process_message_query(queries.get_inbox_messages(user))
|
||||
sent = _process_message_query(queries.get_sent(user))
|
||||
messages = _load_messages(list(chain(inbox, sent)))
|
||||
return compute_message_trees(messages)
|
||||
|
||||
def sr_messages_key(sr_id):
|
||||
return 'sr_messages_conversation_' + str(sr_id)
|
||||
|
||||
def sr_messages_lock_key(sr_id):
|
||||
return 'sr_messages_conversation_lock_' + str(sr_id)
|
||||
|
||||
|
||||
def subreddit_messages(sr, update = False):
|
||||
key = sr_messages_key(sr._id)
|
||||
trees = g.permacache.get(key)
|
||||
if not trees or update:
|
||||
trees = subreddit_messages_nocache(sr)
|
||||
g.permacache.set(key, trees)
|
||||
return trees
|
||||
|
||||
def moderator_messages(user):
|
||||
from r2.models import Subreddit
|
||||
sr_ids = Subreddit.reverse_moderator_ids(user)
|
||||
|
||||
def multi_load_tree(sr_ids):
|
||||
srs = Subreddit._byID(sr_ids, return_dict = False)
|
||||
res = {}
|
||||
for sr in srs:
|
||||
trees = subreddit_messages_nocache(sr)
|
||||
if trees:
|
||||
res[sr._id] = trees
|
||||
return res
|
||||
|
||||
res = sgm(g.permacache, sr_ids, miss_fn = multi_load_tree,
|
||||
prefix = sr_messages_key(""))
|
||||
|
||||
return sorted(chain(*res.values()), key = tree_sort_fn, reverse = True)
|
||||
|
||||
def subreddit_messages_nocache(sr):
|
||||
"""
|
||||
Just like user_messages, but avoiding the cache
|
||||
"""
|
||||
from r2.lib.db import queries
|
||||
inbox = _process_message_query(queries.get_subreddit_messages(sr))
|
||||
messages = _load_messages(inbox)
|
||||
return compute_message_trees(messages)
|
||||
|
||||
|
||||
def add_sr_message_nolock(sr_id, message):
|
||||
return _add_message_nolock(sr_messages_key(sr_id), message)
|
||||
|
||||
def sr_conversation(sr, parent):
|
||||
trees = dict(subreddit_messages(sr))
|
||||
return _conversation(trees, parent)
|
||||
|
||||
|
||||
def compute_message_trees(messages):
|
||||
from r2.models import Message
|
||||
roots = set()
|
||||
|
||||
47
r2/r2/lib/contrib/discount-1.6.0/COPYRIGHT
Normal file
47
r2/r2/lib/contrib/discount-1.6.0/COPYRIGHT
Normal file
@@ -0,0 +1,47 @@
|
||||
->Copyright (C) 2007 David Loren Parsons.
|
||||
All rights reserved.<-
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation files
|
||||
(the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicence, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions, and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and in the same place and form as other
|
||||
copyright, license and disclaimer information.
|
||||
|
||||
3. The end-user documentation included with the redistribution, if
|
||||
any, must include the following acknowledgment:
|
||||
|
||||
This product includes software developed by
|
||||
David Loren Parsons <http://www.pell.portland.or.us/~orc>
|
||||
|
||||
in the same place and form as other third-party acknowledgments.
|
||||
Alternately, this acknowledgment may appear in the software
|
||||
itself, in the same form and location as other such third-party
|
||||
acknowledgments.
|
||||
|
||||
4. Except as contained in this notice, the name of David Loren
|
||||
Parsons shall not be used in advertising or otherwise to promote
|
||||
the sale, use or other dealings in this Software without prior
|
||||
written authorization from David Loren Parsons.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL DAVID LOREN PARSONS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
33
r2/r2/lib/contrib/discount-1.6.0/CREDITS
Normal file
33
r2/r2/lib/contrib/discount-1.6.0/CREDITS
Normal file
@@ -0,0 +1,33 @@
|
||||
Discount is primarily my work, but it has only reached the point
|
||||
where it is via contributions, critiques, and bug reports from a
|
||||
host of other people, some of which are listed before. If your
|
||||
name isn't on this list, please remind me
|
||||
-david parsons (orc@pell.chi.il.us)
|
||||
|
||||
|
||||
Josh Wood -- Plan9 support.
|
||||
Mike Schiraldi -- Reddit style automatic links, MANY MANY MANY
|
||||
bug reports about boundary conditions and
|
||||
places where I didn't get it right.
|
||||
Jjgod Jiang -- Table of contents support.
|
||||
Petite Abeille -- Many bug reports about places where I didn't
|
||||
get it right.
|
||||
Tim Channon -- inspiration for the `mkd_xhtmlpage()` function
|
||||
Christian Herenz-- Many bug reports regarding my implementation of
|
||||
`[]()` and `![]()`
|
||||
A.S.Bradbury -- Portability bug reports for 64 bit systems.
|
||||
Joyent -- Loan of a solaris box so I could get discount
|
||||
working under solaris.
|
||||
Ryan Tomayko -- Portability requests (and the rdiscount ruby
|
||||
binding.)
|
||||
yidabu -- feedback on the documentation, bug reports
|
||||
against utf-8 support.
|
||||
Pierre Joye -- bug reports, php discount binding.
|
||||
Masayoshi Sekimura- perl discount binding.
|
||||
Jeremy Hinegardner- bug reports about list handling.
|
||||
Andrew White -- bug reports about the format of generated urls.
|
||||
Steve Huff -- bug reports about Makefile portability (for Fink)
|
||||
Ignacio Burgue?o-- bug reports about `>%class%`
|
||||
Henrik Nyh -- bug reports about embedded html handling.
|
||||
|
||||
|
||||
61
r2/r2/lib/contrib/discount-1.6.0/Csio.c
Normal file
61
r2/r2/lib/contrib/discount-1.6.0/Csio.c
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "cstring.h"
|
||||
#include "markdown.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
|
||||
/* putc() into a cstring
|
||||
*/
|
||||
void
|
||||
Csputc(int c, Cstring *iot)
|
||||
{
|
||||
EXPAND(*iot) = c;
|
||||
}
|
||||
|
||||
|
||||
/* printf() into a cstring
|
||||
*/
|
||||
int
|
||||
Csprintf(Cstring *iot, char *fmt, ...)
|
||||
{
|
||||
va_list ptr;
|
||||
int siz=100;
|
||||
|
||||
do {
|
||||
RESERVE(*iot, siz);
|
||||
va_start(ptr, fmt);
|
||||
siz = vsnprintf(T(*iot)+S(*iot), ALLOCATED(*iot)-S(*iot), fmt, ptr);
|
||||
va_end(ptr);
|
||||
} while ( siz > (ALLOCATED(*iot)-S(*iot)) );
|
||||
|
||||
S(*iot) += siz;
|
||||
return siz;
|
||||
}
|
||||
|
||||
|
||||
/* write() into a cstring
|
||||
*/
|
||||
int
|
||||
Cswrite(Cstring *iot, char *bfr, int size)
|
||||
{
|
||||
RESERVE(*iot, size);
|
||||
memcpy(T(*iot)+S(*iot), bfr, size);
|
||||
S(*iot) += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/* reparse() into a cstring
|
||||
*/
|
||||
void
|
||||
Csreparse(Cstring *iot, char *buf, int size, int flags)
|
||||
{
|
||||
MMIOT f;
|
||||
___mkd_initmmiot(&f, 0);
|
||||
___mkd_reparse(buf, size, 0, &f);
|
||||
___mkd_emblock(&f);
|
||||
SUFFIX(*iot, T(f.out), S(f.out));
|
||||
___mkd_freemmiot(&f, 0);
|
||||
}
|
||||
41
r2/r2/lib/contrib/discount-1.6.0/INSTALL
Normal file
41
r2/r2/lib/contrib/discount-1.6.0/INSTALL
Normal file
@@ -0,0 +1,41 @@
|
||||
|
||||
HOW TO BUILD AND INSTALL DISCOUNT
|
||||
|
||||
1) Unpacking the distribution
|
||||
|
||||
The DISCOUNT sources are distributed in tarballs. After extracting from
|
||||
the tarball, you should end up with all the source and build files in the
|
||||
directory
|
||||
discount-(version)
|
||||
|
||||
2) Installing the distribution
|
||||
|
||||
DISCOUNT uses configure.sh to set itself up for compilation. To run
|
||||
configure, just do ``./configure.sh'' and it will check your system for
|
||||
build dependencies and build makefiles for you. If configure.sh finishes
|
||||
without complaint, you can then do a ``make'' to compile everything and a
|
||||
``make install'' to install the binaries.
|
||||
|
||||
Configure.sh has a few options that can be set:
|
||||
|
||||
--src=DIR where the source lives (.)
|
||||
--prefix=DIR where to install the final product (/usr/local)
|
||||
--execdir=DIR where to put executables (prefix/bin)
|
||||
--sbindir=DIR where to put static executables (prefix/sbin)
|
||||
--confdir=DIR where to put configuration information (/etc)
|
||||
--libdir=DIR where to put libraries (prefix/lib)
|
||||
--libexecdir=DIR where to put private executables
|
||||
--mandir=DIR where to put manpages
|
||||
--enable-dl-tag Use the DL tag extension
|
||||
--enable-pandoc-header Use pandoc-style header blocks
|
||||
--enable-superscript A^B expands to A<sup>B</sup>
|
||||
--enable-amalloc Use a debugging memory allocator (to detect leaks)
|
||||
--relaxed-emphasis Don't treat _ in the middle of a word as emphasis
|
||||
--with-tabstops=N Set tabstops to N characters (default is 4)
|
||||
|
||||
3) Installing sample programs and manpages
|
||||
|
||||
The standard ``make install'' rule just installs the binaries. If you
|
||||
want to install the sample programs, they are installed with
|
||||
``make install.samples''; to install manpages, ``make install.man''.
|
||||
A shortcut to install everything is ``make install.everything''
|
||||
96
r2/r2/lib/contrib/discount-1.6.0/Makefile
Normal file
96
r2/r2/lib/contrib/discount-1.6.0/Makefile
Normal file
@@ -0,0 +1,96 @@
|
||||
CC=cc -I. -L.
|
||||
AR=/usr/bin/ar
|
||||
RANLIB=/usr/bin/ranlib
|
||||
|
||||
BINDIR=/usr/local/bin
|
||||
MANDIR=/usr/local/man
|
||||
LIBDIR=/usr/local/lib
|
||||
INCDIR=/usr/local/include
|
||||
|
||||
PGMS=markdown
|
||||
SAMPLE_PGMS=mkd2html makepage
|
||||
SAMPLE_PGMS+= theme
|
||||
MKDLIB=libmarkdown.a
|
||||
OBJS=mkdio.o markdown.o dumptree.o generate.o \
|
||||
resource.o docheader.o version.o toc.o css.o \
|
||||
xml.o Csio.o xmlpage.o
|
||||
|
||||
all: $(PGMS) $(SAMPLE_PGMS)
|
||||
|
||||
install: $(PGMS)
|
||||
/usr/bin/install -s -m 755 $(PGMS) $(DESTDIR)/$(BINDIR)
|
||||
/usr/bin/install -m 444 $(MKDLIB) $(DESTDIR)/$(LIBDIR)
|
||||
/usr/bin/install -m 444 mkdio.h $(DESTDIR)/$(INCDIR)
|
||||
|
||||
install.everything: install install.samples install.man
|
||||
|
||||
install.samples: $(SAMPLE_PGMS) install
|
||||
/usr/bin/install -s -m 755 $(SAMPLE_PGMS) $(DESTDIR)/$(BINDIR)
|
||||
/home/raldi/reddit/r2/r2/lib/contrib/discount-1.6.0/config.md $(DESTDIR)/$(MANDIR)/man1
|
||||
/usr/bin/install -m 444 theme.1 $(DESTDIR)/$(MANDIR)/man1
|
||||
|
||||
install.man:
|
||||
/home/raldi/reddit/r2/r2/lib/contrib/discount-1.6.0/config.md $(DESTDIR)/$(MANDIR)/man3
|
||||
/usr/bin/install -m 444 mkd-functions.3 markdown.3 mkd-line.3 $(DESTDIR)/$(MANDIR)/man3
|
||||
for x in mkd_line mkd_generateline; do \
|
||||
( echo '.\"' ; echo ".so man3/mkd-line.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3;\
|
||||
done
|
||||
for x in mkd_in mkd_string; do \
|
||||
( echo '.\"' ; echo ".so man3/markdown.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3;\
|
||||
done
|
||||
for x in mkd_compile mkd_css mkd_generatecss mkd_generatehtml mkd_cleanup mkd_doc_title mkd_doc_author mkd_doc_date; do \
|
||||
( echo '.\"' ; echo ".so man3/mkd-functions.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3; \
|
||||
done
|
||||
/home/raldi/reddit/r2/r2/lib/contrib/discount-1.6.0/config.md $(DESTDIR)/$(MANDIR)/man7
|
||||
/usr/bin/install -m 444 markdown.7 mkd-extensions.7 $(DESTDIR)/$(MANDIR)/man7
|
||||
/home/raldi/reddit/r2/r2/lib/contrib/discount-1.6.0/config.md $(DESTDIR)/$(MANDIR)/man1
|
||||
/usr/bin/install -m 444 markdown.1 $(DESTDIR)/$(MANDIR)/man1
|
||||
|
||||
install.everything: install install.man
|
||||
|
||||
version.o: version.c VERSION
|
||||
$(CC) -DVERSION=\"`cat VERSION`\" -c version.c
|
||||
|
||||
markdown: main.o $(MKDLIB)
|
||||
$(CC) -o markdown main.o -lmarkdown
|
||||
|
||||
# example programs
|
||||
theme: theme.o $(MKDLIB) mkdio.h
|
||||
$(CC) -o theme theme.o -lmarkdown
|
||||
|
||||
|
||||
mkd2html: mkd2html.o $(MKDLIB) mkdio.h
|
||||
$(CC) -o mkd2html mkd2html.o -lmarkdown
|
||||
|
||||
makepage: makepage.c $(MKDLIB) mkdio.h
|
||||
$(CC) -o makepage makepage.c -lmarkdown
|
||||
|
||||
main.o: main.c mkdio.h config.h
|
||||
$(CC) -I. -c main.c
|
||||
|
||||
$(MKDLIB): $(OBJS)
|
||||
$(AR) crv $(MKDLIB) $(OBJS)
|
||||
$(RANLIB) $(MKDLIB)
|
||||
|
||||
test: $(PGMS) echo cols
|
||||
@for x in tests/*.t; do \
|
||||
sh $$x || exit 1; \
|
||||
done
|
||||
|
||||
cols: tools/cols.c
|
||||
$(CC) -o cols tools/cols.c
|
||||
echo: tools/echo.c
|
||||
$(CC) -o echo tools/echo.c
|
||||
|
||||
clean:
|
||||
rm -f $(PGMS) $(SAMPLE_PGMS) *.o $(MKDLIB)
|
||||
|
||||
distclean spotless: clean
|
||||
rm -f Makefile version.c markdown.1 config.cmd config.sub config.h config.mak config.log config.md
|
||||
|
||||
markdown.o: markdown.c config.h cstring.h markdown.h
|
||||
generate.o: generate.c config.h cstring.h markdown.h
|
||||
dumptree.o: dumptree.c cstring.h markdown.h
|
||||
mkdio.o: mkdio.c mkdio.h cstring.h config.h
|
||||
xmlpage.o: xmlpage.c mkdio.h cstring.h config.h
|
||||
toc.o: toc.c mkdio.h cstring.h config.h
|
||||
96
r2/r2/lib/contrib/discount-1.6.0/Makefile.in
Normal file
96
r2/r2/lib/contrib/discount-1.6.0/Makefile.in
Normal file
@@ -0,0 +1,96 @@
|
||||
CC=@CC@ -I. -L.
|
||||
AR=@AR@
|
||||
RANLIB=@RANLIB@
|
||||
|
||||
BINDIR=@exedir@
|
||||
MANDIR=@mandir@
|
||||
LIBDIR=@libdir@
|
||||
INCDIR=@prefix@/include
|
||||
|
||||
PGMS=markdown
|
||||
SAMPLE_PGMS=mkd2html makepage
|
||||
@THEME@SAMPLE_PGMS+= theme
|
||||
MKDLIB=libmarkdown.a
|
||||
OBJS=mkdio.o markdown.o dumptree.o generate.o \
|
||||
resource.o docheader.o version.o toc.o css.o \
|
||||
xml.o Csio.o xmlpage.o @AMALLOC@
|
||||
|
||||
all: $(PGMS) $(SAMPLE_PGMS)
|
||||
|
||||
install: $(PGMS)
|
||||
@INSTALL_PROGRAM@ $(PGMS) $(DESTDIR)/$(BINDIR)
|
||||
@INSTALL_DATA@ $(MKDLIB) $(DESTDIR)/$(LIBDIR)
|
||||
@INSTALL_DATA@ mkdio.h $(DESTDIR)/$(INCDIR)
|
||||
|
||||
install.everything: install install.samples install.man
|
||||
|
||||
install.samples: $(SAMPLE_PGMS) install
|
||||
@INSTALL_PROGRAM@ $(SAMPLE_PGMS) $(DESTDIR)/$(BINDIR)
|
||||
@INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man1
|
||||
@INSTALL_DATA@ theme.1 $(DESTDIR)/$(MANDIR)/man1
|
||||
|
||||
install.man:
|
||||
@INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man3
|
||||
@INSTALL_DATA@ mkd-functions.3 markdown.3 mkd-line.3 $(DESTDIR)/$(MANDIR)/man3
|
||||
for x in mkd_line mkd_generateline; do \
|
||||
( echo '.\"' ; echo ".so man3/mkd-line.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3;\
|
||||
done
|
||||
for x in mkd_in mkd_string; do \
|
||||
( echo '.\"' ; echo ".so man3/markdown.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3;\
|
||||
done
|
||||
for x in mkd_compile mkd_css mkd_generatecss mkd_generatehtml mkd_cleanup mkd_doc_title mkd_doc_author mkd_doc_date; do \
|
||||
( echo '.\"' ; echo ".so man3/mkd-functions.3" ) > $(DESTDIR)/$(MANDIR)/man3/$$x.3; \
|
||||
done
|
||||
@INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man7
|
||||
@INSTALL_DATA@ markdown.7 mkd-extensions.7 $(DESTDIR)/$(MANDIR)/man7
|
||||
@INSTALL_DIR@ $(DESTDIR)/$(MANDIR)/man1
|
||||
@INSTALL_DATA@ markdown.1 $(DESTDIR)/$(MANDIR)/man1
|
||||
|
||||
install.everything: install install.man
|
||||
|
||||
version.o: version.c VERSION
|
||||
$(CC) -DVERSION=\"`cat VERSION`\" -c version.c
|
||||
|
||||
markdown: main.o $(MKDLIB)
|
||||
$(CC) -o markdown main.o -lmarkdown @LIBS@
|
||||
|
||||
# example programs
|
||||
@THEME@theme: theme.o $(MKDLIB) mkdio.h
|
||||
@THEME@ $(CC) -o theme theme.o -lmarkdown @LIBS@
|
||||
|
||||
|
||||
mkd2html: mkd2html.o $(MKDLIB) mkdio.h
|
||||
$(CC) -o mkd2html mkd2html.o -lmarkdown @LIBS@
|
||||
|
||||
makepage: makepage.c $(MKDLIB) mkdio.h
|
||||
$(CC) -o makepage makepage.c -lmarkdown @LIBS@
|
||||
|
||||
main.o: main.c mkdio.h config.h
|
||||
$(CC) -I. -c main.c
|
||||
|
||||
$(MKDLIB): $(OBJS)
|
||||
$(AR) crv $(MKDLIB) $(OBJS)
|
||||
$(RANLIB) $(MKDLIB)
|
||||
|
||||
test: $(PGMS) echo cols
|
||||
@for x in tests/*.t; do \
|
||||
sh $$x || exit 1; \
|
||||
done
|
||||
|
||||
cols: tools/cols.c
|
||||
$(CC) -o cols tools/cols.c
|
||||
echo: tools/echo.c
|
||||
$(CC) -o echo tools/echo.c
|
||||
|
||||
clean:
|
||||
rm -f $(PGMS) $(SAMPLE_PGMS) *.o $(MKDLIB)
|
||||
|
||||
distclean spotless: clean
|
||||
rm -f @GENERATED_FILES@ @CONFIGURE_FILES@
|
||||
|
||||
markdown.o: markdown.c config.h cstring.h markdown.h
|
||||
generate.o: generate.c config.h cstring.h markdown.h
|
||||
dumptree.o: dumptree.c cstring.h markdown.h
|
||||
mkdio.o: mkdio.c mkdio.h cstring.h config.h
|
||||
xmlpage.o: xmlpage.c mkdio.h cstring.h config.h
|
||||
toc.o: toc.c mkdio.h cstring.h config.h
|
||||
40
r2/r2/lib/contrib/discount-1.6.0/Plan9/README
Normal file
40
r2/r2/lib/contrib/discount-1.6.0/Plan9/README
Normal file
@@ -0,0 +1,40 @@
|
||||
% Discount on Plan 9
|
||||
% Josh Wood
|
||||
% 2009-06-12
|
||||
|
||||
# *Discount* Markdown compiler on Plan 9
|
||||
|
||||
## Build
|
||||
|
||||
% CONFIG='--enable-all-features' mk config
|
||||
% mk install
|
||||
% markdown -V
|
||||
markdown: discount X.Y.Z DL_TAG HEADER DEBUG SUPERSCRIPT RELAXED DIV
|
||||
|
||||
`--enable-all-features` may be replaced by zero or more of:
|
||||
|
||||
--enable-dl-tag Use the DL tag extension
|
||||
--enable-pandoc-header Use pandoc-style header blocks
|
||||
--enable-superscript A^B becomes A<sup>B</sup>
|
||||
--enable-amalloc Enable memory allocation debugging
|
||||
--relaxed-emphasis underscores aren't special in the middle of words
|
||||
--with-tabstops=N Set tabstops to N characters (default is 4)
|
||||
--enable-div Enable >%id% divisions
|
||||
--enable-alpha-list Enable (a)/(b)/(c) lists
|
||||
--enable-all-features Turn on all stable optional features
|
||||
|
||||
## Notes
|
||||
|
||||
The supplied mkfile merely drives Discount's own configure script and
|
||||
then APE's *psh* environment to build the Discount source, then copies
|
||||
the result(s) to locations appropriate for system-wide use on Plan 9.
|
||||
There are a few other *mk*(1) targets:
|
||||
|
||||
`install.libs`: Discount includes a C library and header.
|
||||
Installation is optional. Plan 9 binaries are statically linked.
|
||||
|
||||
`install.man`: Add manual pages for markdown(1) and (6).
|
||||
|
||||
`install.progs`: Extra programs. *makepage* writes complete XHTML
|
||||
documents, rather than fragments. *mkd2html* is similar, but produces
|
||||
HTML.
|
||||
169
r2/r2/lib/contrib/discount-1.6.0/Plan9/markdown.1
Normal file
169
r2/r2/lib/contrib/discount-1.6.0/Plan9/markdown.1
Normal file
@@ -0,0 +1,169 @@
|
||||
.TH MARKDOWN 1
|
||||
.SH NAME
|
||||
markdown \- convert Markdown text to HTML
|
||||
.SH SYNOPSIS
|
||||
.B markdown
|
||||
[
|
||||
.B -dTV
|
||||
]
|
||||
[
|
||||
.BI -b " url-base
|
||||
]
|
||||
[
|
||||
.BI -F " bitmap
|
||||
]
|
||||
[
|
||||
.BI -f " flags
|
||||
]
|
||||
[
|
||||
.BI -o " ofile
|
||||
]
|
||||
[
|
||||
.BI -s " text
|
||||
]
|
||||
[
|
||||
.BI -t " text
|
||||
]
|
||||
[
|
||||
.I file
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.I markdown
|
||||
utility reads the
|
||||
.IR Markdown (6)-formatted
|
||||
.I file
|
||||
(or standard input) and writes its
|
||||
.SM HTML
|
||||
fragment representation on standard output.
|
||||
.PP
|
||||
The options are:
|
||||
.TF dfdoptions
|
||||
.TP
|
||||
.BI -b " url-base
|
||||
Links in source begining with
|
||||
.B /
|
||||
will be prefixed with
|
||||
.I url-base
|
||||
in the output.
|
||||
.TP
|
||||
.B -d
|
||||
Instead of printing an
|
||||
.SM HTML
|
||||
fragment, print a parse tree.
|
||||
.TP
|
||||
.BI -F " bitmap
|
||||
Set translation flags.
|
||||
.I Bitmap
|
||||
is a bit map of the various configuration options described in
|
||||
.IR markdown (2).
|
||||
.TP
|
||||
.BI -f " flags
|
||||
Set or clear various translation
|
||||
.IR flags ,
|
||||
described below.
|
||||
.I Flags
|
||||
are in a comma-delimited list, with an optional
|
||||
.B +
|
||||
(set) prefix on each flag.
|
||||
.TP
|
||||
.BI -o " ofile
|
||||
Write the generated
|
||||
.SM HTML
|
||||
to
|
||||
.IR ofile .
|
||||
.TP
|
||||
.BI -s " text
|
||||
Use the
|
||||
.IR markdown (2)
|
||||
function to format the
|
||||
.I text
|
||||
on standard input.
|
||||
.TP
|
||||
.B -T
|
||||
Under
|
||||
.B -f
|
||||
.BR toc ,
|
||||
print the table of contents as an unordered list before the usual
|
||||
.SM HTML
|
||||
output.
|
||||
.TP
|
||||
.BI -t " text
|
||||
Use
|
||||
.IR mkd_text
|
||||
(in
|
||||
.IR markdown (2))
|
||||
to format
|
||||
.I text
|
||||
instead of processing standard input with
|
||||
.IR markdown .
|
||||
.TP
|
||||
.B -V
|
||||
Show version number and configuration. If the version includes the string
|
||||
.BR DL_TAG ,
|
||||
.I markdown
|
||||
was configured with definition list support. If the version includes the string
|
||||
.BR HEADER ,
|
||||
.I markdown
|
||||
was configured to support pandoc header blocks.
|
||||
.PD
|
||||
.SS TRANSLATION FLAGS
|
||||
The translation flags understood by
|
||||
.B -f
|
||||
are:
|
||||
.TF \ noheader
|
||||
.TP
|
||||
.B noimage
|
||||
Don't allow image tags.
|
||||
.TP
|
||||
.B nolinks
|
||||
Don't allow links.
|
||||
.TP
|
||||
.B nohtml
|
||||
Don't allow any embedded HTML.
|
||||
.TP
|
||||
.B cdata
|
||||
Generate valid XML output.
|
||||
.TP
|
||||
.B noheader
|
||||
Do not process pandoc headers.
|
||||
.TP
|
||||
.B notables
|
||||
Do not process the syntax extension for tables.
|
||||
.TP
|
||||
.B tabstops
|
||||
Use Markdown-standard 4-space tabstops.
|
||||
.TP
|
||||
.B strict
|
||||
Disable superscript and relaxed emphasis.
|
||||
.TP
|
||||
.B relax
|
||||
Enable superscript and relaxed emphasis (the default).
|
||||
.TP
|
||||
.B toc
|
||||
Enable table of contents support, generated from headings (in
|
||||
.IR markdown (6))
|
||||
in the source.
|
||||
.TP
|
||||
.B 1.0
|
||||
Revert to Markdown 1.0 compatibility.
|
||||
.PD
|
||||
.PP
|
||||
For example,
|
||||
.B -f nolinks,quot
|
||||
tells
|
||||
.I markdown
|
||||
not to allow
|
||||
.B <a>
|
||||
tags, and to expand double quotes.
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/discount
|
||||
.SH SEE ALSO
|
||||
.IR markdown (2),
|
||||
.IR markdown (6)
|
||||
.PP
|
||||
http://daringfireball.net/projects/markdown/,
|
||||
``Markdown''.
|
||||
.SH DIAGNOSTICS
|
||||
.I Markdown
|
||||
exits 0 on success and >0 if an error occurs.
|
||||
332
r2/r2/lib/contrib/discount-1.6.0/Plan9/markdown.2
Normal file
332
r2/r2/lib/contrib/discount-1.6.0/Plan9/markdown.2
Normal file
@@ -0,0 +1,332 @@
|
||||
.TH MARKDOWN 2
|
||||
.SH NAME
|
||||
mkd_in, mkd_string, markdown, mkd_compile, mkd_css, mkd_generatecss,
|
||||
mkd_document, mkd_generatehtml, mkd_xhtmlpage, mkd_toc, mkd_generatetoc,
|
||||
mkd_cleanup, mkd_doc_title, mkd_doc_author, mkd_doc_date, mkd_line,
|
||||
mkd_generateline \- convert Markdown text to HTML
|
||||
.SH SYNOPSIS
|
||||
.ta \w'MMIOT* 'u
|
||||
.B #include <mkdio.h>
|
||||
.PP
|
||||
.B
|
||||
MMIOT* mkd_in(FILE *input, int flags)
|
||||
.PP
|
||||
.B
|
||||
MMIOT* mkd_string(char *buf, int size, int flags)
|
||||
.PP
|
||||
.B
|
||||
int markdown(MMIOT *doc, FILE *output, int flags)
|
||||
.PP
|
||||
.B
|
||||
int mkd_compile(MMIOT *document, int flags)
|
||||
.PP
|
||||
.B
|
||||
int mkd_css(MMIOT *document, char **doc)
|
||||
.PP
|
||||
.B
|
||||
int mkd_generatecss(MMIOT *document, FILE *output)
|
||||
.PP
|
||||
.B
|
||||
int mkd_document(MMIOT *document, char **doc)
|
||||
.PP
|
||||
.B
|
||||
int mkd_generatehtml(MMIOT *document, FILE *output)
|
||||
.PP
|
||||
.B
|
||||
int mkd_xhtmlpage(MMIOT *document, int flags, FILE *output)
|
||||
.PP
|
||||
.B
|
||||
int mkd_toc(MMIOT *document, char **doc)
|
||||
.PP
|
||||
.B
|
||||
int mkd_generatetoc(MMIOT *document, FILE *output)
|
||||
.PP
|
||||
.B
|
||||
void mkd_cleanup(MMIOT*);
|
||||
.PP
|
||||
.B
|
||||
char* mkd_doc_title(MMIOT*)
|
||||
.PP
|
||||
.B
|
||||
char* mkd_doc_author(MMIOT*)
|
||||
.PP
|
||||
.B
|
||||
char* mkd_doc_date(MMIOT*)
|
||||
.PP
|
||||
.B
|
||||
int mkd_line(char *string, int size, char **doc, int flags)
|
||||
.PP
|
||||
.B
|
||||
int mkd_generateline(char *string, int size, FILE *output, int flags)
|
||||
.PD
|
||||
.PP
|
||||
.SH DESCRIPTION
|
||||
These functions convert
|
||||
.IR Markdown (6)
|
||||
text into
|
||||
.SM HTML
|
||||
markup.
|
||||
.PP
|
||||
.I Mkd_in
|
||||
reads the text referenced by pointer to
|
||||
.B FILE
|
||||
.I input
|
||||
and returns a pointer to an
|
||||
.B MMIOT
|
||||
structure of the form expected by
|
||||
.I markdown
|
||||
and the other converters.
|
||||
.I Mkd_string
|
||||
accepts one
|
||||
.I string
|
||||
and returns a pointer to
|
||||
.BR MMIOT .
|
||||
.PP
|
||||
After such preparation,
|
||||
.I markdown
|
||||
converts
|
||||
.I doc
|
||||
and writes the result to
|
||||
.IR output ,
|
||||
while
|
||||
.I mkd_compile
|
||||
transforms
|
||||
.I document
|
||||
in-place.
|
||||
.PP
|
||||
One or more of the following
|
||||
.I flags
|
||||
(combined with
|
||||
.BR OR )
|
||||
control
|
||||
.IR markdown 's
|
||||
processing of
|
||||
.IR doc :
|
||||
.TF MKD_NOIMAGE
|
||||
.TP
|
||||
.B MKD_NOIMAGE
|
||||
Do not process
|
||||
.B ![]
|
||||
and remove
|
||||
.B <img>
|
||||
tags from the output.
|
||||
.TP
|
||||
.B MKD_NOLINKS
|
||||
Do not process
|
||||
.B []
|
||||
and remove
|
||||
.B <a>
|
||||
tags from the output.
|
||||
.TP
|
||||
.B MKD_NOPANTS
|
||||
Suppress Smartypants-style replacement of quotes, dashes, or ellipses.
|
||||
.TP
|
||||
.B MKD_STRICT
|
||||
Disable superscript and relaxed emphasis processing if configured; otherwise a no-op.
|
||||
.TP
|
||||
.B MKD_TAGTEXT
|
||||
Process as inside an
|
||||
.SM HTML
|
||||
tag: no
|
||||
.BR <em> ,
|
||||
no
|
||||
.BR <bold> ,
|
||||
no
|
||||
.SM HTML
|
||||
or
|
||||
.B []
|
||||
expansion.
|
||||
.TP
|
||||
.B MKD_NO_EXT
|
||||
Don't process pseudo-protocols (in
|
||||
.IR markdown (6)).
|
||||
.TP
|
||||
.B MKD_CDATA
|
||||
Generate code for
|
||||
.SM XML
|
||||
.B ![CDATA[...]]
|
||||
element.
|
||||
.TP
|
||||
.B MKD_NOHEADER
|
||||
Don't process Pandoc-style headers.
|
||||
.TP
|
||||
.B MKD_TABSTOP
|
||||
When reading documents, expand tabs to 4 spaces, overriding any compile-time configuration.
|
||||
.TP
|
||||
.B MKD_TOC
|
||||
Label headings for use with the
|
||||
.I mkd_generatetoc
|
||||
and
|
||||
.I mkd_toc
|
||||
functions.
|
||||
.TP
|
||||
.B MKD_1_COMPAT
|
||||
MarkdownTest_1.0 compatibility. Trim trailing spaces from first line of code blocks and disable implicit reference links (in
|
||||
.IR markdown (6)).
|
||||
.TP
|
||||
.B MKD_AUTOLINK
|
||||
Greedy
|
||||
.SM URL
|
||||
generation. When set, any
|
||||
.SM URL
|
||||
is converted to a hyperlink, even those not encased in
|
||||
.BR <> .
|
||||
.TP
|
||||
.B MKD_SAFELINK
|
||||
Don't make hyperlinks from
|
||||
.B [][]
|
||||
links that have unknown
|
||||
.SM URL
|
||||
protocol types.
|
||||
.TP
|
||||
.B MKD_NOTABLES
|
||||
Do not process the syntax extension for tables (in
|
||||
.IR markdown (6)).
|
||||
.TP
|
||||
.B MKD_EMBED
|
||||
All of
|
||||
.BR MKD_NOLINKS ,
|
||||
.BR MKD_NOIMAGE ,
|
||||
and
|
||||
.BR MKD_TAGTEXT .
|
||||
.PD
|
||||
.PP
|
||||
This implementation supports
|
||||
Pandoc-style
|
||||
headers and inline
|
||||
.SM CSS
|
||||
.B <style>
|
||||
blocks, but
|
||||
.I markdown
|
||||
does not access the data provided by these extensions.
|
||||
The following functions do, and allow other manipulations.
|
||||
.PP
|
||||
Given a pointer to
|
||||
.B MMIOT
|
||||
prepared by
|
||||
.I mkd_in
|
||||
or
|
||||
.IR mkd_string ,
|
||||
.I mkd_compile
|
||||
compiles the
|
||||
.I document
|
||||
into
|
||||
.BR <style> ,
|
||||
Pandoc, and
|
||||
.SM HTML
|
||||
sections. It accepts the
|
||||
.I flags
|
||||
described for
|
||||
.IR markdown ,
|
||||
above.
|
||||
.PP
|
||||
Once compiled, the document particulars can be read and written:
|
||||
.PP
|
||||
.I Mkd_css
|
||||
allocates a string and populates it with any
|
||||
.B <style>
|
||||
sections from the document.
|
||||
.I Mkd_generatecss
|
||||
writes any
|
||||
.B <style>
|
||||
sections to
|
||||
.IR output .
|
||||
.PP
|
||||
.I Mkd_document
|
||||
points
|
||||
.I doc
|
||||
to the
|
||||
.B MMIOT
|
||||
.IR document ,
|
||||
returning
|
||||
.IR document 's
|
||||
size.
|
||||
.PP
|
||||
.I Mkd_generatehtml
|
||||
writes the rest of the
|
||||
.I document
|
||||
to the
|
||||
.IR output .
|
||||
.PP
|
||||
.IR Mkd_doc_title ,
|
||||
.IR mkd_doc_author ,
|
||||
and
|
||||
.I mkd_doc_date
|
||||
read the contents of any Pandoc header.
|
||||
.PP
|
||||
.I Mkd_xhtmlpage
|
||||
writes an
|
||||
.SM XHTML
|
||||
page representation of the document.
|
||||
It accepts the
|
||||
.I flags
|
||||
described for
|
||||
.IR markdown ,
|
||||
above.
|
||||
.PP
|
||||
.I Mkd_toc
|
||||
.IR malloc s
|
||||
a buffer into which it writes an outline, in the form of a
|
||||
.B <ul>
|
||||
element populated with
|
||||
.BR <li> s
|
||||
each containing a link to successive headings in the
|
||||
.IR document .
|
||||
It returns the size of that string.
|
||||
.I Mkd_generatetoc
|
||||
is similar,
|
||||
but writes the outline to the
|
||||
.I output
|
||||
referenced by a pointer to
|
||||
.BR FILE .
|
||||
.PP
|
||||
.I Mkd_cleanup
|
||||
deletes a processed
|
||||
.BR MMIOT .
|
||||
.PP
|
||||
The last two functions convert a single line of markdown source, for example a page title or a signature.
|
||||
.I Mkd_line
|
||||
allocates a buffer into which it writes an
|
||||
.SM HTML
|
||||
fragment representation of the
|
||||
.IR string .
|
||||
.I Mkd_generateline
|
||||
writes the result to
|
||||
.IR output .
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/discount
|
||||
.SH SEE ALSO
|
||||
.IR markdown (1),
|
||||
.IR markdown (6)
|
||||
.SH DIAGNOSTICS
|
||||
The
|
||||
.I mkd_in
|
||||
and
|
||||
.I mkd_string
|
||||
functions return a pointer to
|
||||
.B MMIOT
|
||||
on success, null on failure.
|
||||
.IR Markdown ,
|
||||
.IR mkd_compile ,
|
||||
.IR mkd_style ,
|
||||
and
|
||||
.I mkd_generatehtml
|
||||
return
|
||||
.B 0
|
||||
on success,
|
||||
.B -1
|
||||
otherwise.
|
||||
.SH BUGS
|
||||
Error handling is minimal at best.
|
||||
.PP
|
||||
The
|
||||
.B MMIOT
|
||||
created by
|
||||
.I mkd_string
|
||||
is deleted by the
|
||||
.I markdown
|
||||
function.
|
||||
.PP
|
||||
This is an
|
||||
.SM APE
|
||||
library.
|
||||
543
r2/r2/lib/contrib/discount-1.6.0/Plan9/markdown.6
Normal file
543
r2/r2/lib/contrib/discount-1.6.0/Plan9/markdown.6
Normal file
@@ -0,0 +1,543 @@
|
||||
.TH MARKDOWN 6
|
||||
.SH NAME
|
||||
Markdown \- text formatting syntax
|
||||
.SH DESCRIPTION
|
||||
Markdown
|
||||
is a text markup syntax for machine conversion to
|
||||
the more complex
|
||||
.SM HTML
|
||||
or
|
||||
.SM XHTML
|
||||
markup languages.
|
||||
It is intended to be easy to read and to write, with
|
||||
emphasis on readability.
|
||||
A Markdown-formatted document should be publishable as-is,
|
||||
in plain text, without the formatting distracting the reader.
|
||||
.PP
|
||||
The biggest source of inspiration for Markdown's
|
||||
syntax is the format of plain text email. The markup is comprised entirely
|
||||
of punctuation characters, chosen so as to look like what they mean.
|
||||
Asterisks around a word look like
|
||||
.IR *emphasis* .
|
||||
Markdown lists look like lists. Even
|
||||
blockquotes look like quoted passages of text, assuming the reader has
|
||||
used email.
|
||||
.PP
|
||||
.SS Block Elements
|
||||
.TF W
|
||||
.PD
|
||||
.TP
|
||||
Paragraphs and Line Breaks
|
||||
A paragraph is one or more consecutive lines of text, separated
|
||||
by one or more blank lines. (A blank line is any line that looks like a
|
||||
blank line -- a line containing nothing but spaces or tabs is considered
|
||||
blank.) Normal paragraphs should not be indented with spaces or tabs.
|
||||
.IP
|
||||
Lines may be freely broken for readability; Markdown
|
||||
does not translate source line breaks to
|
||||
.B <br />
|
||||
tags. To request generation of
|
||||
.B <br />
|
||||
in the output, end a line with two or more spaces, then a newline.
|
||||
.TP
|
||||
Headings
|
||||
Headings can be marked in two ways, called
|
||||
.I setext
|
||||
and
|
||||
.IR atx .
|
||||
.IP
|
||||
Setext-style headings are
|
||||
``underlined'' using equal signs (for first-level
|
||||
headings) and dashes (for second-level headings).
|
||||
.IP
|
||||
Atx-style headings use 1-6 hash characters at the start of the line,
|
||||
corresponding to
|
||||
.SM HTML
|
||||
.BR <h^(1-6)^> .
|
||||
Optional closing hashes may follow
|
||||
the heading text.
|
||||
.TP
|
||||
Blockquotes
|
||||
Lines beginning with
|
||||
.B >
|
||||
are output in blockquotes.
|
||||
Blockquotes can be nested
|
||||
by multiple levels of
|
||||
.BR >> .
|
||||
Blockquotes can contain other Markdown elements, including
|
||||
headings, lists, and code blocks.
|
||||
.TP
|
||||
Lists
|
||||
Markdown supports ordered (numbered) and unordered (bulleted) lists.
|
||||
List markers typically start at the left margin, but may be indented by
|
||||
up to three spaces.
|
||||
List markers must be followed by one or more spaces
|
||||
or a tab, then the list item text.
|
||||
A newline terminates each list item.
|
||||
.IP
|
||||
Unordered lists use asterisks, pluses, and hyphens interchangeably as
|
||||
list markers.
|
||||
.IP
|
||||
Ordered lists use integers followed by periods as list markers.
|
||||
The order of the integers is not interpreted,
|
||||
but the list should begin with
|
||||
.BR 1 .
|
||||
.IP
|
||||
If list items are separated by blank lines, Markdown will wrap each list
|
||||
item in
|
||||
.B <p>
|
||||
tags in the
|
||||
.SM HTML
|
||||
output.
|
||||
.IP
|
||||
List items may consist of multiple paragraphs.
|
||||
Each subsequent
|
||||
paragraph within a list item must be indented by either 4 spaces
|
||||
or one tab.
|
||||
To put a blockquote within a list item, the blockquote's
|
||||
.B >
|
||||
marker needs to be indented.
|
||||
To put a code block within a list item, the code block needs
|
||||
to be indented
|
||||
.I twice
|
||||
-- 8 spaces or two tabs.
|
||||
.TP
|
||||
Code Blocks
|
||||
To produce a code block, indent every line of the
|
||||
block by at least 4 spaces or 1 tab.
|
||||
A code block continues until it reaches a line that is not indented.
|
||||
.IP
|
||||
Rather than forming normal paragraphs, the lines
|
||||
of a code block are interpreted literally.
|
||||
Regular Markdown syntax is not processed within code blocks.
|
||||
Markdown wraps a code block in both
|
||||
.B <pre>
|
||||
and
|
||||
.B <code>
|
||||
tags.
|
||||
One level of indentation -- 4
|
||||
spaces or 1 tab -- is removed from each line of the code block in
|
||||
the output.
|
||||
.TP
|
||||
Horizontal Rules
|
||||
Produce a horizontal rule tag
|
||||
.RB ( <hr\ /> )
|
||||
by placing three or
|
||||
more hyphens, asterisks, or underscores on a line by themselves.
|
||||
.SS Span Elements
|
||||
.TF W
|
||||
.PD
|
||||
.TP
|
||||
Links
|
||||
Markdown supports two styles of links:
|
||||
.I inline
|
||||
and
|
||||
.IR reference .
|
||||
In both styles, the link text is delimited by square brackets
|
||||
.RB ( [] ).
|
||||
To create an inline link, use a set of regular parentheses immediately
|
||||
after the link text's closing square bracket.
|
||||
Inside the parentheses,
|
||||
put the link URL, along with an optional
|
||||
title for the link surrounded in double quotes.
|
||||
For example:
|
||||
.IP
|
||||
.EX
|
||||
An [example](http://example.com/ "Title") inline link.
|
||||
.EE
|
||||
.IP
|
||||
Reference-style links use a second set of square brackets, inside
|
||||
which you place a label of your choosing to identify the link:
|
||||
.IP
|
||||
.EX
|
||||
An [example][id] reference-style link.
|
||||
.EE
|
||||
.IP
|
||||
The label is then assigned a value on its own line, anywhere in the document:
|
||||
.IP
|
||||
.EX
|
||||
[id]: http://example.com/ "Optional Title"
|
||||
.EE
|
||||
.IP
|
||||
Link label names may consist of letters, numbers, spaces, and
|
||||
punctuation.
|
||||
Labels are not case sensitive.
|
||||
An empty label bracket
|
||||
set after a reference-style link implies the link label is equivalent to
|
||||
the link text.
|
||||
A URL value can then be assigned to the link by referencing
|
||||
the link text as the label name.
|
||||
.TP
|
||||
Emphasis
|
||||
Markdown treats asterisks
|
||||
.RB ( * )
|
||||
and underscores
|
||||
.RB ( _ )
|
||||
as indicators of emphasis.
|
||||
Text surrounded with single asterisks or underscores
|
||||
will be wrapped with an
|
||||
.SM HTML
|
||||
.B <em>
|
||||
tag.
|
||||
Double asterisks or underscores generate an
|
||||
.SM HTML
|
||||
.B <strong>
|
||||
tag.
|
||||
.TP
|
||||
Code
|
||||
To indicate a span of code, wrap it with backtick quotes
|
||||
.RB ( ` ).
|
||||
Unlike a code block, a code span indicates code within a
|
||||
normal paragraph.
|
||||
To include a literal backtick character within a code span, you can use
|
||||
multiple backticks as the opening and closing delimiters:
|
||||
.IP
|
||||
.EX
|
||||
``There is a literal backtick (`) here.``
|
||||
.EE
|
||||
.TP
|
||||
Images
|
||||
Markdown image syntax is intended to resemble that
|
||||
for links, allowing for two styles, once again
|
||||
.I inline
|
||||
and
|
||||
.IR reference .
|
||||
The syntax is as for each respective style of link, described above, but
|
||||
prefixed with an exclamation mark character
|
||||
.RB ( ! ).
|
||||
Inline image syntax looks like this:
|
||||
.IP
|
||||
.EX
|
||||

|
||||
.EE
|
||||
.IP
|
||||
That is:
|
||||
An exclamation mark;
|
||||
followed by a set of square brackets containing the `alt'
|
||||
attribute text for the image;
|
||||
followed by a set of parentheses containing the URL or path to
|
||||
the image, and an optional `title' attribute enclosed in double
|
||||
or single quotes.
|
||||
.IP
|
||||
Reference-style image syntax looks like this:
|
||||
.IP
|
||||
.EX
|
||||
![Alt text][id]
|
||||
.EE
|
||||
.IP
|
||||
Where
|
||||
.I id
|
||||
is a label used as for reference-style URL links, described above.
|
||||
.SS Convenience
|
||||
.TF W
|
||||
.PD
|
||||
.TP
|
||||
Automatic Links
|
||||
There is a shortcut style for creating ``automatic''
|
||||
links for URLs and email addresses.
|
||||
Surround the URL
|
||||
or address with angle brackets.
|
||||
.TP
|
||||
Backslash Escapes
|
||||
Use backslash escapes to generate literal
|
||||
characters which would otherwise have special meaning in Markdown's
|
||||
formatting syntax.
|
||||
.TP
|
||||
Inline HTML
|
||||
For markup that is not covered by Markdown's
|
||||
syntax, simply use the
|
||||
.SM HTML
|
||||
directly.
|
||||
The only restrictions are that block-level
|
||||
.SM HTML
|
||||
elements --
|
||||
.BR <div> ,
|
||||
.BR <table> ,
|
||||
.BR <pre> ,
|
||||
.BR <p> ,
|
||||
etc. -- must be separated from surrounding
|
||||
content by blank lines, and the start and end tags of the block should
|
||||
not be indented with tabs or spaces. Markdown formatting syntax is
|
||||
not processed within block-level
|
||||
.SM HTML
|
||||
tags.
|
||||
.IP
|
||||
Span-level
|
||||
.SM HTML
|
||||
tags -- e.g.
|
||||
.BR <span> ,
|
||||
.BR <cite> ,
|
||||
or
|
||||
.B <del>
|
||||
-- can be
|
||||
used anywhere in a Markdown
|
||||
paragraph, list item, or heading.
|
||||
It is permitted to use
|
||||
.SM HTML
|
||||
tags instead of Markdown formatting; e.g.
|
||||
.SM HTML
|
||||
.B <a>
|
||||
or
|
||||
.B <img>
|
||||
tags instead of Markdown's
|
||||
link or image syntax.
|
||||
Unlike block-level
|
||||
.SM HTML
|
||||
tags, Markdown
|
||||
syntax
|
||||
.I is
|
||||
processed within the elements of span-level tags.
|
||||
.TP
|
||||
Automatic Special Character Escapes
|
||||
To be displayed literally in a user agent, the characters
|
||||
.B <
|
||||
and
|
||||
.B &
|
||||
must appear as escaped entities in
|
||||
.SM HTML
|
||||
source, e.g.
|
||||
.B <
|
||||
and
|
||||
.BR & .
|
||||
Markdown
|
||||
allows natural use of these characters, taking care of
|
||||
the necessary escaping.
|
||||
The ampersand part of a directly-used
|
||||
.SM HTML
|
||||
entity remains unchanged; otherwise it will be translated
|
||||
into
|
||||
.BR & .
|
||||
Inside code spans and blocks, angle brackets and
|
||||
ampersands are always encoded automatically.
|
||||
This makes it easy to use Markdown to write about
|
||||
.SM HTML
|
||||
code.
|
||||
.PP
|
||||
.SS Smarty Pants
|
||||
The
|
||||
.IR markdown (1)
|
||||
utility transforms a few plain text symbols into their typographically-fancier
|
||||
.SM HTML
|
||||
entity equivalents.
|
||||
These are extensions to the standard Markdown syntax.
|
||||
.TF W
|
||||
.PD
|
||||
.TP
|
||||
Punctuation
|
||||
Input single- and double-quotes are transformed
|
||||
into ``curly'' quote entities in the output (e.g.,
|
||||
.B 'text'
|
||||
becomes
|
||||
.BR ‘text’ ).
|
||||
Input double-dashes
|
||||
.RB ( -- )
|
||||
and triple-dashes become en- and em-dashes, respectively,
|
||||
while a series of three dots
|
||||
.RB ( ... )
|
||||
in the input becomes an ellipsis entity
|
||||
.RB ( … )
|
||||
in the
|
||||
.SM HTML
|
||||
output.
|
||||
.TP
|
||||
Symbols
|
||||
Three other transformations replace the common plain-text shorthands
|
||||
.BR (c) ,
|
||||
.BR (r) ,
|
||||
and
|
||||
.BR (tm)
|
||||
from the input with their respective
|
||||
.SM HTML
|
||||
entities. (As in
|
||||
.B (c)
|
||||
becoming
|
||||
.BR © ,
|
||||
the Copyright symbol entity.)
|
||||
.TP
|
||||
Fractions
|
||||
A small set of plain-text shorthands for fractions is recognized.
|
||||
.B 1/4
|
||||
becomes
|
||||
.BR ¼ ,
|
||||
for example. These fraction notations are replaced with their
|
||||
.SM HTML
|
||||
entity equivalents:
|
||||
.BR 1/4 ,
|
||||
.BR 1/2 ,
|
||||
.BR 3/4 .
|
||||
.B 1/4th
|
||||
and
|
||||
.B 3/4ths
|
||||
are replaced with their entity and the indicated ordinal suffix letters.
|
||||
.PP
|
||||
Like the basic Markdown syntax, none of the ``Smarty Pants'' extensions are processed
|
||||
inside code blocks or spans.
|
||||
.PP
|
||||
.SS Discount Extensions
|
||||
.IR Markdown (1)
|
||||
recognizes some extensions to the Markdown format,
|
||||
many of them adopted or adapted from other Markdown
|
||||
interpreters or document formatting systems.
|
||||
.TF W
|
||||
.PD
|
||||
.TP
|
||||
Pandoc Headers
|
||||
If
|
||||
.I markdown
|
||||
was configured with
|
||||
.BR --enable-pandoc-header ,
|
||||
the markdown source can have a 3-line Pandoc header in the format of
|
||||
.IP
|
||||
.EX
|
||||
% Title
|
||||
% Author
|
||||
% Date
|
||||
.EE
|
||||
.IP
|
||||
whose data is available to the
|
||||
.IR mkd_doc_title ,
|
||||
.IR mkd_doc_author ,
|
||||
and
|
||||
.I mkd_doc_date
|
||||
(in
|
||||
.IR markdown (2))
|
||||
functions.
|
||||
.TP
|
||||
Embedded Stylesheets
|
||||
Stylesheets may be defined and modified in a
|
||||
.B <style>
|
||||
block. A style block is parsed like any other block-level
|
||||
.SM HTML;
|
||||
.B <style>
|
||||
starting on column 1, raw
|
||||
.SM HTML
|
||||
(or, in this case,
|
||||
.SM CSS \)
|
||||
following it, and either ending with a
|
||||
.B </style>
|
||||
at the end of the line or at the beginning of a subsequent line.
|
||||
.IP
|
||||
Style blocks apply to the entire document regardless of where they are defined.
|
||||
.TP
|
||||
Image Dimensions
|
||||
Image specification has been extended with an argument describing image dimensions:
|
||||
.BI = height x width.
|
||||
For an image 400 pixels high and 300 wide, the new syntax is:
|
||||
.IP
|
||||
.EX
|
||||

|
||||
.EE
|
||||
.TP
|
||||
Pseudo-Protocols
|
||||
Pseudo-protocols that may replace the common
|
||||
.B http:
|
||||
or
|
||||
.B mailto:
|
||||
have been added to the link syntax described above.
|
||||
.IP
|
||||
.BR abbr :
|
||||
Text following is used as the
|
||||
.B title
|
||||
attribute of an
|
||||
.B abbr
|
||||
tag wrapping the link text. So
|
||||
.B [LT](abbr:Link Text)
|
||||
gives
|
||||
.B <abbr title="Link Text">LT</abbr>.
|
||||
.IP
|
||||
.BR id :
|
||||
The link text is marked up and written to the output, wrapped with
|
||||
.B <a id=text following>
|
||||
and
|
||||
.BR </a> .
|
||||
.IP
|
||||
.BR class :
|
||||
The link text is marked up and written to the output, wrapped with
|
||||
.B <span class=text following>
|
||||
and
|
||||
.BR </span> .
|
||||
.IP
|
||||
.BR raw :
|
||||
Text following is written to the output with no further processing.
|
||||
The link text is discarded.
|
||||
.TP
|
||||
Alphabetic Lists
|
||||
If
|
||||
.I markdown
|
||||
was configured with
|
||||
.BR --enable-alpha-list ,
|
||||
.IP
|
||||
.EX
|
||||
a. this
|
||||
b. is
|
||||
c. an alphabetic
|
||||
d. list
|
||||
.EE
|
||||
.IP
|
||||
yields an
|
||||
.SM HTML
|
||||
.B ol
|
||||
ordered list.
|
||||
.TP
|
||||
Definition Lists
|
||||
If configured with
|
||||
.BR --enable-dl-tag ,
|
||||
markup for definition lists is enabled. A definition list item is defined as
|
||||
.IP
|
||||
.EX
|
||||
=term=
|
||||
definition
|
||||
.EE
|
||||
.TP
|
||||
Tables
|
||||
Tables are specified with a pipe
|
||||
.RB ( | )
|
||||
and dash
|
||||
.RB ( - )
|
||||
marking. The markdown text
|
||||
.IP
|
||||
.EX
|
||||
header0|header1
|
||||
-------|-------
|
||||
textA|textB
|
||||
textC|textD
|
||||
.EE
|
||||
.IP
|
||||
will produce an
|
||||
.SM HTML
|
||||
.B table
|
||||
of two columns and three rows.
|
||||
A header row is designated by ``underlining'' with dashes.
|
||||
Declare a column's alignment by affixing a colon
|
||||
.RB ( : )
|
||||
to the left or right end of the dashes underlining its header.
|
||||
In the output, this
|
||||
yields the corresponding value for the
|
||||
.B align
|
||||
attribute on each
|
||||
.B td
|
||||
cell in the column.
|
||||
A colon at both ends of a column's header dashes indicates center alignment.
|
||||
.TP
|
||||
Relaxed Emphasis
|
||||
If configured with
|
||||
.BR --relaxed-emphasis ,
|
||||
the rules for emphasis are changed so that a single
|
||||
.B _
|
||||
will not count as an emphasis character in the middle of a word.
|
||||
This is useful for documenting some code where
|
||||
.B _
|
||||
appears frequently, and would normally require a backslash escape.
|
||||
.PD
|
||||
.SH SEE ALSO
|
||||
.IR markdown (1),
|
||||
.IR markdown (2)
|
||||
.PP
|
||||
http://daringfireball.net/projects/markdown/syntax/,
|
||||
``Markdown: Syntax''.
|
||||
.PP
|
||||
http://daringfireball.net/projects/smartypants/,
|
||||
``Smarty Pants''.
|
||||
.PP
|
||||
http://michelf.com/projects/php-markdown/extra/#table,
|
||||
``PHP Markdown Extra: Tables''.
|
||||
37
r2/r2/lib/contrib/discount-1.6.0/Plan9/mkfile
Normal file
37
r2/r2/lib/contrib/discount-1.6.0/Plan9/mkfile
Normal file
@@ -0,0 +1,37 @@
|
||||
BIN=/$objtype/bin
|
||||
CC='cc -D_BSD_EXTENSION'
|
||||
|
||||
markdown:
|
||||
ape/psh -c 'cd .. && make'
|
||||
|
||||
none:V: markdown
|
||||
|
||||
test: markdown
|
||||
ape/psh -c 'cd ..&& make test'
|
||||
|
||||
install: markdown
|
||||
cp ../markdown $BIN/markdown
|
||||
|
||||
install.progs: install
|
||||
cp ../makepage $BIN/makepage
|
||||
cp ../mkd2html $BIN/mkd2html
|
||||
|
||||
install.libs: install
|
||||
cp ../mkdio.h /sys/include/ape/mkdio.h
|
||||
cp ../libmarkdown.a /$objtype/lib/ape/libmarkdown.a
|
||||
|
||||
install.man: install
|
||||
cp markdown.1 /sys/man/1/markdown
|
||||
cp markdown.2 /sys/man/2/markdown
|
||||
cp markdown.6 /sys/man/6/markdown
|
||||
|
||||
installall:V: install.libs install.man install.progs
|
||||
|
||||
config:
|
||||
ape/psh -c 'cd .. && ./configure.sh $CONFIG'
|
||||
|
||||
clean:
|
||||
ape/psh -c 'cd .. && make clean'
|
||||
|
||||
nuke:
|
||||
ape/psh -c 'cd .. && make distclean'
|
||||
16
r2/r2/lib/contrib/discount-1.6.0/README
Normal file
16
r2/r2/lib/contrib/discount-1.6.0/README
Normal file
@@ -0,0 +1,16 @@
|
||||
DISCOUNT is a implementation of John Gruber's Markdown markup
|
||||
language. It implements, as far as I can tell, all of the
|
||||
language as described in
|
||||
<http://daringfireball.net/projects/markdown/syntax>
|
||||
and passes the Markdown test suite at
|
||||
<http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip>
|
||||
|
||||
DISCOUNT is free software written by David Parsons <orc@pell.chi.il.us>;
|
||||
it is released under a BSD-style license that allows you to do
|
||||
as you wish with it as long as you don't attempt to claim it as
|
||||
your own work.
|
||||
|
||||
Most of the programs included in the DISCOUNT distribution have
|
||||
manual pages describing how they work.
|
||||
|
||||
The file INSTALL describes how to build and install discount
|
||||
1
r2/r2/lib/contrib/discount-1.6.0/VERSION
Normal file
1
r2/r2/lib/contrib/discount-1.6.0/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
1.6.1
|
||||
111
r2/r2/lib/contrib/discount-1.6.0/amalloc.c
Normal file
111
r2/r2/lib/contrib/discount-1.6.0/amalloc.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* debugging malloc()/realloc()/calloc()/free() that attempts
|
||||
* to keep track of just what's been allocated today.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAGIC 0x1f2e3d4c
|
||||
|
||||
struct alist { int magic, size; struct alist *next, *last; };
|
||||
|
||||
static struct alist list = { 0, 0, 0, 0 };
|
||||
|
||||
static int mallocs=0;
|
||||
static int reallocs=0;
|
||||
static int frees=0;
|
||||
|
||||
void *
|
||||
acalloc(int size, int count)
|
||||
{
|
||||
struct alist *ret = calloc(size + sizeof(struct alist), count);
|
||||
|
||||
if ( ret ) {
|
||||
ret->magic = MAGIC;
|
||||
ret->size = size * count;
|
||||
if ( list.next ) {
|
||||
ret->next = list.next;
|
||||
ret->last = &list;
|
||||
ret->next->last = ret;
|
||||
list.next = ret;
|
||||
}
|
||||
else {
|
||||
ret->last = ret->next = &list;
|
||||
list.next = list.last = ret;
|
||||
}
|
||||
++mallocs;
|
||||
return ret+1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
amalloc(int size)
|
||||
{
|
||||
return acalloc(size,1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
afree(void *ptr)
|
||||
{
|
||||
struct alist *p2 = ((struct alist*)ptr)-1;
|
||||
|
||||
if ( p2->magic == MAGIC ) {
|
||||
p2->last->next = p2->next;
|
||||
p2->next->last = p2->last;
|
||||
++frees;
|
||||
free(p2);
|
||||
}
|
||||
else
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
arealloc(void *ptr, int size)
|
||||
{
|
||||
struct alist *p2 = ((struct alist*)ptr)-1;
|
||||
struct alist save;
|
||||
|
||||
if ( p2->magic == MAGIC ) {
|
||||
save.next = p2->next;
|
||||
save.last = p2->last;
|
||||
p2 = realloc(p2, sizeof(*p2) + size);
|
||||
|
||||
if ( p2 ) {
|
||||
p2->size = size;
|
||||
p2->next->last = p2;
|
||||
p2->last->next = p2;
|
||||
++reallocs;
|
||||
return p2+1;
|
||||
}
|
||||
else {
|
||||
save.next->last = save.last;
|
||||
save.last->next = save.next;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return realloc(ptr, size);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
adump()
|
||||
{
|
||||
struct alist *p;
|
||||
|
||||
|
||||
for ( p = list.next; p && (p != &list); p = p->next ) {
|
||||
fprintf(stderr, "allocated: %d byte%s\n", p->size, (p->size==1) ? "" : "s");
|
||||
fprintf(stderr, " [%.*s]\n", p->size, p+1);
|
||||
}
|
||||
|
||||
if ( getenv("AMALLOC_STATISTICS") ) {
|
||||
fprintf(stderr, "%d malloc%s\n", mallocs, (mallocs==1)?"":"s");
|
||||
fprintf(stderr, "%d realloc%s\n", reallocs, (reallocs==1)?"":"s");
|
||||
fprintf(stderr, "%d free%s\n", frees, (frees==1)?"":"s");
|
||||
}
|
||||
}
|
||||
29
r2/r2/lib/contrib/discount-1.6.0/amalloc.h
Normal file
29
r2/r2/lib/contrib/discount-1.6.0/amalloc.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* debugging malloc()/realloc()/calloc()/free() that attempts
|
||||
* to keep track of just what's been allocated today.
|
||||
*/
|
||||
#ifndef AMALLOC_D
|
||||
#define AMALLOC_D
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_AMALLOC
|
||||
|
||||
extern void *amalloc(int);
|
||||
extern void *acalloc(int,int);
|
||||
extern void *arealloc(void*,int);
|
||||
extern void afree(void*);
|
||||
extern void adump();
|
||||
|
||||
#define malloc amalloc
|
||||
#define calloc acalloc
|
||||
#define realloc arealloc
|
||||
#define free afree
|
||||
|
||||
#else
|
||||
|
||||
#define adump() (void)1
|
||||
|
||||
#endif
|
||||
|
||||
#endif/*AMALLOC_D*/
|
||||
2
r2/r2/lib/contrib/discount-1.6.0/config.cmd
Executable file
2
r2/r2/lib/contrib/discount-1.6.0/config.cmd
Executable file
@@ -0,0 +1,2 @@
|
||||
#! /bin/sh
|
||||
./configure.sh
|
||||
29
r2/r2/lib/contrib/discount-1.6.0/config.h
Normal file
29
r2/r2/lib/contrib/discount-1.6.0/config.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* configuration for markdown, generated Fri Jan 29 13:52:23 PST 2010
|
||||
* by raldi@zork
|
||||
*/
|
||||
#ifndef __AC_MARKDOWN_D
|
||||
#define __AC_MARKDOWN_D 1
|
||||
|
||||
|
||||
#define OS_LINUX 1
|
||||
#define DWORD unsigned long
|
||||
#define WORD unsigned short
|
||||
#define BYTE unsigned char
|
||||
#define HAVE_BASENAME 1
|
||||
#define HAVE_LIBGEN_H 1
|
||||
#define HAVE_PWD_H 1
|
||||
#define HAVE_GETPWUID 1
|
||||
#define HAVE_SRANDOM 1
|
||||
#define INITRNG(x) srandom((unsigned int)x)
|
||||
#define HAVE_BZERO 1
|
||||
#define HAVE_RANDOM 1
|
||||
#define COINTOSS() (random()&1)
|
||||
#define HAVE_STRCASECMP 1
|
||||
#define HAVE_STRNCASECMP 1
|
||||
#define HAVE_FCHDIR 1
|
||||
#define TABSTOP 4
|
||||
#define HAVE_MALLOC_H 1
|
||||
#define PATH_SED "/bin/sed"
|
||||
|
||||
#endif/* __AC_MARKDOWN_D */
|
||||
31
r2/r2/lib/contrib/discount-1.6.0/config.log
Normal file
31
r2/r2/lib/contrib/discount-1.6.0/config.log
Normal file
@@ -0,0 +1,31 @@
|
||||
[echo -n] works
|
||||
Configuring for [markdown]
|
||||
Looking for cpp
|
||||
CPP=[/lib/cpp], CPPFLAGS=[]
|
||||
looking for install
|
||||
(/usr/bin/install)
|
||||
checking out the C compiler
|
||||
checking for "volatile" keyword
|
||||
checking for "const" keyword
|
||||
defining WORD & DWORD scalar types
|
||||
/tmp/pd18169.c: In function 'main':
|
||||
/tmp/pd18169.c:13: warning: incompatible implicit declaration of built-in function 'exit'
|
||||
/tmp/ngc18169.c: In function 'main':
|
||||
/tmp/ngc18169.c:5: warning: initialization makes pointer from integer without a cast
|
||||
/tmp/ngc18169.c:6: warning: initialization makes pointer from integer without a cast
|
||||
looking for header libgen.h
|
||||
looking for header pwd.h
|
||||
looking for the getpwuid function
|
||||
looking for the srandom function
|
||||
looking for the bzero function
|
||||
/tmp/ngc18169.c: In function 'main':
|
||||
/tmp/ngc18169.c:4: warning: incompatible implicit declaration of built-in function 'bzero'
|
||||
looking for the random function
|
||||
looking for the strcasecmp function
|
||||
looking for the strncasecmp function
|
||||
looking for the fchdir function
|
||||
looking for header malloc.h
|
||||
sed is /bin/sed
|
||||
generating Makefile
|
||||
generating version.c
|
||||
generating markdown.1
|
||||
1
r2/r2/lib/contrib/discount-1.6.0/config.mak
Normal file
1
r2/r2/lib/contrib/discount-1.6.0/config.mak
Normal file
@@ -0,0 +1 @@
|
||||
HAVE_SED = 1
|
||||
5
r2/r2/lib/contrib/discount-1.6.0/config.md
Executable file
5
r2/r2/lib/contrib/discount-1.6.0/config.md
Executable file
@@ -0,0 +1,5 @@
|
||||
#! /bin/sh
|
||||
# script generated Fri Jan 29 13:52:24 PST 2010 by configure.sh
|
||||
|
||||
test -d "$1" || mkdir -p "$1"
|
||||
exit 0
|
||||
27
r2/r2/lib/contrib/discount-1.6.0/config.sub
vendored
Normal file
27
r2/r2/lib/contrib/discount-1.6.0/config.sub
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
s;@CPP@;/lib/cpp;g
|
||||
s;@CPPFLAGS@;;g
|
||||
s;@INSTALL@;/usr/bin/install;g
|
||||
s;@INSTALL_PROGRAM@;/usr/bin/install -s -m 755;g
|
||||
s;@INSTALL_DATA@;/usr/bin/install -m 444;g
|
||||
s;@INSTALL_DIR@;/home/raldi/reddit/r2/r2/lib/contrib/discount-1.6.0/config.md;g
|
||||
s;@CC@;cc;g
|
||||
s;@AR@;/usr/bin/ar;g
|
||||
s;@RANLIB@;/usr/bin/ranlib;g
|
||||
s;@THEME@;;g
|
||||
s;@TABSTOP@;4;g
|
||||
s;@AMALLOC@;;g
|
||||
s;@STRICT@;.\";g
|
||||
s;@LIBS@;;g
|
||||
s;@CONFIGURE_FILES@;config.cmd config.sub config.h config.mak config.log config.md;g
|
||||
s;@GENERATED_FILES@;Makefile version.c markdown.1;g
|
||||
s;@CFLAGS@;-g;g
|
||||
s;@LDFLAGS@;-g;g
|
||||
s;@srcdir@;/home/raldi/reddit/r2/r2/lib/contrib/discount-1.6.0;g
|
||||
s;@prefix@;/usr/local;g
|
||||
s;@exedir@;/usr/local/bin;g
|
||||
s;@sbindir@;/usr/local/sbin;g
|
||||
s;@libdir@;/usr/local/lib;g
|
||||
s;@libexec@;/usr/local/lib;g
|
||||
s;@confdir@;/etc;g
|
||||
s;@mandir@;/usr/local/man;g
|
||||
s;@SED@;/bin/sed;g
|
||||
1465
r2/r2/lib/contrib/discount-1.6.0/configure.inc
Executable file
1465
r2/r2/lib/contrib/discount-1.6.0/configure.inc
Executable file
File diff suppressed because it is too large
Load Diff
152
r2/r2/lib/contrib/discount-1.6.0/configure.sh
Executable file
152
r2/r2/lib/contrib/discount-1.6.0/configure.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#! /bin/sh
|
||||
|
||||
# local options: ac_help is the help message that describes them
|
||||
# and LOCAL_AC_OPTIONS is the script that interprets them. LOCAL_AC_OPTIONS
|
||||
# is a script that's processed with eval, so you need to be very careful to
|
||||
# make certain that what you quote is what you want to quote.
|
||||
|
||||
# load in the configuration file
|
||||
#
|
||||
ac_help='--enable-dl-tag Use the DL tag extension
|
||||
--enable-pandoc-header Use pandoc-style header blocks
|
||||
--enable-superscript A^B becomes A<sup>B</sup>
|
||||
--enable-amalloc Enable memory allocation debugging
|
||||
--relaxed-emphasis underscores aren'\''t special in the middle of words
|
||||
--with-tabstops=N Set tabstops to N characters (default is 4)
|
||||
--enable-div Enable >%id% divisions
|
||||
--enable-alpha-list Enable (a)/(b)/(c) lists
|
||||
--enable-all-features Turn on all stable optional features'
|
||||
|
||||
LOCAL_AC_OPTIONS='
|
||||
set=`locals $*`;
|
||||
if [ "$set" ]; then
|
||||
eval $set
|
||||
shift 1
|
||||
else
|
||||
ac_error=T;
|
||||
fi'
|
||||
|
||||
locals() {
|
||||
K=`echo $1 | $AC_UPPERCASE`
|
||||
case "$K" in
|
||||
--RELAXED-EMPHAS*)
|
||||
echo RELAXED_EMPHASIS=T
|
||||
;;
|
||||
--ENABLE-ALL|--ENABLE-ALL-FEATURES)
|
||||
echo WITH_DL_TAG=T
|
||||
echo RELAXED_EMPHASIS=T
|
||||
echo WITH_PANDOC_HEADER=T
|
||||
echo WITH_SUPERSCRIPT=T
|
||||
echo WITH_AMALLOC=T
|
||||
echo WITH_DIV=T
|
||||
#echo WITH_ALPHA_LIST=T
|
||||
;;
|
||||
--ENABLE-*) enable=`echo $K | sed -e 's/--ENABLE-//' | tr '-' '_'`
|
||||
echo WITH_${enable}=T ;;
|
||||
esac
|
||||
}
|
||||
|
||||
TARGET=markdown
|
||||
. ./configure.inc
|
||||
|
||||
AC_INIT $TARGET
|
||||
|
||||
AC_PROG_CC
|
||||
|
||||
case "$AC_CC $AC_CFLAGS" in
|
||||
*-Wall*) AC_DEFINE 'while(x)' 'while( (x) != 0 )'
|
||||
AC_DEFINE 'if(x)' 'if( (x) != 0 )' ;;
|
||||
esac
|
||||
|
||||
AC_PROG ar || AC_FAIL "$TARGET requires ar"
|
||||
AC_PROG ranlib
|
||||
|
||||
AC_C_VOLATILE
|
||||
AC_C_CONST
|
||||
AC_SCALAR_TYPES
|
||||
AC_CHECK_BASENAME
|
||||
|
||||
AC_CHECK_HEADERS sys/types.h pwd.h && AC_CHECK_FUNCS getpwuid
|
||||
|
||||
if AC_CHECK_FUNCS srandom; then
|
||||
AC_DEFINE 'INITRNG(x)' 'srandom((unsigned int)x)'
|
||||
elif AC_CHECK_FUNCS srand; then
|
||||
AC_DEFINE 'INITRNG(x)' 'srand((unsigned int)x)'
|
||||
else
|
||||
AC_DEFINE 'INITRNG(x)' '(void)1'
|
||||
fi
|
||||
|
||||
if AC_CHECK_FUNCS 'bzero((char*)0,0)'; then
|
||||
: # Yay
|
||||
elif AC_CHECK_FUNCS 'memset((char*)0,0,0)'; then
|
||||
AC_DEFINE 'bzero(p,s)' 'memset(p,s,0)'
|
||||
else
|
||||
AC_FAIL "$TARGET requires bzero or memset"
|
||||
fi
|
||||
|
||||
if AC_CHECK_FUNCS random; then
|
||||
AC_DEFINE 'COINTOSS()' '(random()&1)'
|
||||
elif AC_CHECK_FUNCS rand; then
|
||||
AC_DEFINE 'COINTOSS()' '(rand()&1)'
|
||||
else
|
||||
AC_DEFINE 'COINTOSS()' '1'
|
||||
fi
|
||||
|
||||
if AC_CHECK_FUNCS strcasecmp; then
|
||||
:
|
||||
elif AC_CHECK_FUNCS stricmp; then
|
||||
AC_DEFINE strcasecmp stricmp
|
||||
else
|
||||
AC_FAIL "$TARGET requires either strcasecmp() or stricmp()"
|
||||
fi
|
||||
|
||||
if AC_CHECK_FUNCS strncasecmp; then
|
||||
:
|
||||
elif AC_CHECK_FUNCS strnicmp; then
|
||||
AC_DEFINE strncasecmp strnicmp
|
||||
else
|
||||
AC_FAIL "$TARGET requires either strncasecmp() or strnicmp()"
|
||||
fi
|
||||
|
||||
if AC_CHECK_FUNCS fchdir || AC_CHECK_FUNCS getcwd ; then
|
||||
AC_SUB 'THEME' ''
|
||||
else
|
||||
AC_SUB 'THEME' '#'
|
||||
fi
|
||||
|
||||
if [ -z "$WITH_TABSTOPS" ]; then
|
||||
TABSTOP=4
|
||||
elif [ "$WITH_TABSTOPS" -eq 1 ]; then
|
||||
TABSTOP=8
|
||||
else
|
||||
TABSTOP=$WITH_TABSTOPS
|
||||
fi
|
||||
AC_DEFINE 'TABSTOP' $TABSTOP
|
||||
AC_SUB 'TABSTOP' $TABSTOP
|
||||
|
||||
test -z "$WITH_SUPERSCRIPT" || AC_DEFINE 'SUPERSCRIPT' 1
|
||||
test -z "$RELAXED_EMPHASIS" || AC_DEFINE 'RELAXED_EMPHASIS' 1
|
||||
test -z "$WITH_DIV" || AC_DEFINE 'DIV_QUOTE' 1
|
||||
test -z "$WITH_ALPHA_LIST" || AC_DEFINE 'ALPHA_LIST' 1
|
||||
|
||||
|
||||
if [ "$WITH_AMALLOC" ]; then
|
||||
AC_DEFINE 'USE_AMALLOC' 1
|
||||
AC_SUB 'AMALLOC' 'amalloc.o'
|
||||
else
|
||||
AC_SUB 'AMALLOC' ''
|
||||
fi
|
||||
|
||||
if [ "$RELAXED_EMPHASIS" -o "$WITH_SUPERSCRIPT" ]; then
|
||||
AC_SUB 'STRICT' ''
|
||||
else
|
||||
AC_SUB 'STRICT' '.\"'
|
||||
fi
|
||||
|
||||
|
||||
[ "$OS_FREEBSD" -o "$OS_DRAGONFLY" ] || AC_CHECK_HEADERS malloc.h
|
||||
|
||||
[ "$WITH_DL_TAG" ] && AC_DEFINE 'DL_TAG_EXTENSION' '1'
|
||||
[ "$WITH_PANDOC_HEADER" ] && AC_DEFINE 'PANDOC_HEADER' '1'
|
||||
|
||||
AC_OUTPUT Makefile version.c markdown.1
|
||||
76
r2/r2/lib/contrib/discount-1.6.0/css.c
Normal file
76
r2/r2/lib/contrib/discount-1.6.0/css.c
Normal file
@@ -0,0 +1,76 @@
|
||||
/* markdown: a C implementation of John Gruber's Markdown markup language.
|
||||
*
|
||||
* Copyright (C) 2009 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "cstring.h"
|
||||
#include "markdown.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
|
||||
/*
|
||||
* dump out stylesheet sections.
|
||||
*/
|
||||
static void
|
||||
stylesheets(Paragraph *p, Cstring *f)
|
||||
{
|
||||
Line* q;
|
||||
|
||||
for ( ; p ; p = p->next ) {
|
||||
if ( p->typ == STYLE ) {
|
||||
for ( q = p->text; q ; q = q->next )
|
||||
Cswrite(f, T(q->text), S(q->text));
|
||||
Csputc('\n', f);
|
||||
}
|
||||
if ( p->down )
|
||||
stylesheets(p->down, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* dump any embedded styles to a string
|
||||
*/
|
||||
int
|
||||
mkd_css(Document *d, char **res)
|
||||
{
|
||||
Cstring f;
|
||||
|
||||
if ( res && *res && d && d->compiled ) {
|
||||
CREATE(f);
|
||||
RESERVE(f, 100);
|
||||
stylesheets(d->code, &f);
|
||||
|
||||
/* HACK ALERT! HACK ALERT! HACK ALERT! */
|
||||
*res = T(f); /* we know that a T(Cstring) is a character pointer */
|
||||
/* so we can simply pick it up and carry it away, */
|
||||
return S(f); /* leaving the husk of the Ctring on the stack */
|
||||
/* END HACK ALERT */
|
||||
}
|
||||
return EOF;
|
||||
}
|
||||
|
||||
|
||||
/* dump any embedded styles to a file
|
||||
*/
|
||||
int
|
||||
mkd_generatecss(Document *d, FILE *f)
|
||||
{
|
||||
char *res;
|
||||
int written = EOF, size = mkd_css(d, &res);
|
||||
|
||||
if ( size > 0 )
|
||||
written = fwrite(res, size, 1, f);
|
||||
if ( res )
|
||||
free(res);
|
||||
return (written == size) ? size : EOF;
|
||||
}
|
||||
75
r2/r2/lib/contrib/discount-1.6.0/cstring.h
Normal file
75
r2/r2/lib/contrib/discount-1.6.0/cstring.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* two template types: STRING(t) which defines a pascal-style string
|
||||
* of element (t) [STRING(char) is the closest to the pascal string],
|
||||
* and ANCHOR(t) which defines a baseplate that a linked list can be
|
||||
* built up from. [The linked list /must/ contain a ->next pointer
|
||||
* for linking the list together with.]
|
||||
*/
|
||||
#ifndef _CSTRING_D
|
||||
#define _CSTRING_D
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "amalloc.h"
|
||||
|
||||
/* expandable Pascal-style string.
|
||||
*/
|
||||
#define STRING(type) struct { type *text; int size, alloc; }
|
||||
|
||||
#define CREATE(x) T(x) = (void*)(S(x) = (x).alloc = 0)
|
||||
#define EXPAND(x) (S(x)++)[(S(x) < (x).alloc) \
|
||||
? (T(x)) \
|
||||
: (T(x) = T(x) ? realloc(T(x), sizeof T(x)[0] * ((x).alloc += 100)) \
|
||||
: malloc(sizeof T(x)[0] * ((x).alloc += 100)) )]
|
||||
|
||||
#define DELETE(x) ALLOCATED(x) ? (free(T(x)), S(x) = (x).alloc = 0) \
|
||||
: ( S(x) = 0 )
|
||||
#define CLIP(t,i,sz) \
|
||||
( ((i) >= 0) && ((sz) > 0) && (((i)+(sz)) <= S(t)) ) ? \
|
||||
(memmove(&T(t)[i], &T(t)[i+sz], (S(t)-(i+sz)+1)*sizeof(T(t)[0])), \
|
||||
S(t) -= (sz)) : -1
|
||||
|
||||
#define RESERVE(x, sz) T(x) = ((x).alloc > S(x) + (sz) \
|
||||
? T(x) \
|
||||
: T(x) \
|
||||
? realloc(T(x), sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))) \
|
||||
: malloc(sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))))
|
||||
#define SUFFIX(t,p,sz) \
|
||||
memcpy(((S(t) += (sz)) - (sz)) + \
|
||||
(T(t) = T(t) ? realloc(T(t), sizeof T(t)[0] * ((t).alloc += sz)) \
|
||||
: malloc(sizeof T(t)[0] * ((t).alloc += sz))), \
|
||||
(p), sizeof(T(t)[0])*(sz))
|
||||
|
||||
#define PREFIX(t,p,sz) \
|
||||
RESERVE( (t), (sz) ); \
|
||||
if ( S(t) ) { memmove(T(t)+(sz), T(t), S(t)); } \
|
||||
memcpy( T(t), (p), (sz) ); \
|
||||
S(t) += (sz)
|
||||
|
||||
/* reference-style links (and images) are stored in an array
|
||||
*/
|
||||
#define T(x) (x).text
|
||||
#define S(x) (x).size
|
||||
#define ALLOCATED(x) (x).alloc
|
||||
|
||||
/* abstract anchor type that defines a list base
|
||||
* with a function that attaches an element to
|
||||
* the end of the list.
|
||||
*
|
||||
* the list base field is named .text so that the T()
|
||||
* macro will work with it.
|
||||
*/
|
||||
#define ANCHOR(t) struct { t *text, *end; }
|
||||
#define E(t) ((t).end)
|
||||
|
||||
#define ATTACH(t, p) ( T(t) ? ( (E(t)->next = (p)), (E(t) = (p)) ) \
|
||||
: ( (T(t) = E(t) = (p)) ) )
|
||||
|
||||
typedef STRING(char) Cstring;
|
||||
|
||||
extern void Csputc(int, Cstring *);
|
||||
extern int Csprintf(Cstring *, char *, ...);
|
||||
extern int Cswrite(Cstring *, char *, int);
|
||||
extern void Csreparse(Cstring *, char *, int, int);
|
||||
|
||||
#endif/*_CSTRING_D*/
|
||||
43
r2/r2/lib/contrib/discount-1.6.0/docheader.c
Normal file
43
r2/r2/lib/contrib/discount-1.6.0/docheader.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* docheader -- get values from the document header
|
||||
*
|
||||
* Copyright (C) 2007 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cstring.h"
|
||||
#include "markdown.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
#define afterdle(t) (T((t)->text) + (t)->dle)
|
||||
|
||||
char *
|
||||
mkd_doc_title(Document *doc)
|
||||
{
|
||||
if ( doc && doc->headers )
|
||||
return afterdle(doc->headers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
mkd_doc_author(Document *doc)
|
||||
{
|
||||
if ( doc && doc->headers && doc->headers->next )
|
||||
return afterdle(doc->headers->next);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
mkd_doc_date(Document *doc)
|
||||
{
|
||||
if ( doc && doc->headers && doc->headers->next && doc->headers->next->next )
|
||||
return afterdle(doc->headers->next->next);
|
||||
return 0;
|
||||
}
|
||||
151
r2/r2/lib/contrib/discount-1.6.0/dumptree.c
Executable file
151
r2/r2/lib/contrib/discount-1.6.0/dumptree.c
Executable file
@@ -0,0 +1,151 @@
|
||||
/* markdown: a C implementation of John Gruber's Markdown markup language.
|
||||
*
|
||||
* Copyright (C) 2007 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "markdown.h"
|
||||
#include "cstring.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
struct frame {
|
||||
int indent;
|
||||
char c;
|
||||
};
|
||||
|
||||
typedef STRING(struct frame) Stack;
|
||||
|
||||
static char *
|
||||
Pptype(int typ)
|
||||
{
|
||||
switch (typ) {
|
||||
case WHITESPACE: return "whitespace";
|
||||
case CODE : return "code";
|
||||
case QUOTE : return "quote";
|
||||
case MARKUP : return "markup";
|
||||
case HTML : return "html";
|
||||
case DL : return "dl";
|
||||
case UL : return "ul";
|
||||
case OL : return "ol";
|
||||
case LISTITEM : return "item";
|
||||
case HDR : return "header";
|
||||
case HR : return "hr";
|
||||
case TABLE : return "table";
|
||||
case SOURCE : return "source";
|
||||
default : return "mystery node!";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pushpfx(int indent, char c, Stack *sp)
|
||||
{
|
||||
struct frame *q = &EXPAND(*sp);
|
||||
|
||||
q->indent = indent;
|
||||
q->c = c;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
poppfx(Stack *sp)
|
||||
{
|
||||
S(*sp)--;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
changepfx(Stack *sp, char c)
|
||||
{
|
||||
char ch;
|
||||
|
||||
if ( !S(*sp) ) return;
|
||||
|
||||
ch = T(*sp)[S(*sp)-1].c;
|
||||
|
||||
if ( ch == '+' || ch == '|' )
|
||||
T(*sp)[S(*sp)-1].c = c;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
printpfx(Stack *sp, FILE *f)
|
||||
{
|
||||
int i;
|
||||
char c;
|
||||
|
||||
if ( !S(*sp) ) return;
|
||||
|
||||
c = T(*sp)[S(*sp)-1].c;
|
||||
|
||||
if ( c == '+' || c == '-' ) {
|
||||
fprintf(f, "--%c", c);
|
||||
T(*sp)[S(*sp)-1].c = (c == '-') ? ' ' : '|';
|
||||
}
|
||||
else
|
||||
for ( i=0; i < S(*sp); i++ ) {
|
||||
if ( i )
|
||||
fprintf(f, " ");
|
||||
fprintf(f, "%*s%c", T(*sp)[i].indent + 2, " ", T(*sp)[i].c);
|
||||
if ( T(*sp)[i].c == '`' )
|
||||
T(*sp)[i].c = ' ';
|
||||
}
|
||||
fprintf(f, "--");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dumptree(Paragraph *pp, Stack *sp, FILE *f)
|
||||
{
|
||||
int count;
|
||||
Line *p;
|
||||
int d;
|
||||
static char *Begin[] = { 0, "P", "center" };
|
||||
|
||||
while ( pp ) {
|
||||
if ( !pp->next )
|
||||
changepfx(sp, '`');
|
||||
printpfx(sp, f);
|
||||
|
||||
d = fprintf(f, "[%s", Pptype(pp->typ));
|
||||
if ( pp->ident )
|
||||
d += fprintf(f, " %s", pp->ident);
|
||||
if ( pp->align )
|
||||
d += fprintf(f, ", <%s>", Begin[pp->align]);
|
||||
|
||||
for (count=0, p=pp->text; p; ++count, (p = p->next) )
|
||||
;
|
||||
|
||||
if ( count )
|
||||
d += fprintf(f, ", %d line%s", count, (count==1)?"":"s");
|
||||
|
||||
d += fprintf(f, "]");
|
||||
|
||||
if ( pp->down ) {
|
||||
pushpfx(d, pp->down->next ? '+' : '-', sp);
|
||||
dumptree(pp->down, sp, f);
|
||||
poppfx(sp);
|
||||
}
|
||||
else fputc('\n', f);
|
||||
pp = pp->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
mkd_dump(Document *doc, FILE *out, int flags, char *title)
|
||||
{
|
||||
Stack stack;
|
||||
|
||||
if (mkd_compile(doc, flags) ) {
|
||||
|
||||
CREATE(stack);
|
||||
pushpfx(fprintf(out, "%s", title), doc->code->next ? '+' : '-', &stack);
|
||||
dumptree(doc->code, &stack, out);
|
||||
DELETE(stack);
|
||||
|
||||
mkd_cleanup(doc);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
1635
r2/r2/lib/contrib/discount-1.6.0/generate.c
Normal file
1635
r2/r2/lib/contrib/discount-1.6.0/generate.c
Normal file
File diff suppressed because it is too large
Load Diff
211
r2/r2/lib/contrib/discount-1.6.0/main.c
Normal file
211
r2/r2/lib/contrib/discount-1.6.0/main.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* markdown: convert a single markdown document into html
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2007 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <mkdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
#if HAVE_LIBGEN_H
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_BASENAME
|
||||
#include <string.h>
|
||||
|
||||
char*
|
||||
basename(char *p)
|
||||
{
|
||||
char *ret = strrchr(p, '/');
|
||||
|
||||
return ret ? (1+ret) : p;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
char *pgm = "markdown";
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
int off;
|
||||
int flag;
|
||||
} opts[] = {
|
||||
{ "tabstop", 0, MKD_TABSTOP },
|
||||
{ "image", 1, MKD_NOIMAGE },
|
||||
{ "links", 1, MKD_NOLINKS },
|
||||
{ "relax", 1, MKD_STRICT },
|
||||
{ "strict", 0, MKD_STRICT },
|
||||
{ "tables", 1, MKD_NOTABLES },
|
||||
{ "header", 1, MKD_NOHEADER },
|
||||
{ "html", 1, MKD_NOHTML },
|
||||
{ "ext", 1, MKD_NO_EXT },
|
||||
{ "cdata", 0, MKD_CDATA },
|
||||
{ "pants", 1, MKD_NOPANTS },
|
||||
{ "smarty", 1, MKD_NOPANTS },
|
||||
{ "toc", 0, MKD_TOC },
|
||||
{ "autolink",0, MKD_AUTOLINK },
|
||||
{ "safelink",0, MKD_SAFELINK },
|
||||
{ "1.0", 0, MKD_1_COMPAT },
|
||||
} ;
|
||||
|
||||
#define NR(x) (sizeof x / sizeof x[0])
|
||||
|
||||
|
||||
void
|
||||
set(int *flags, char *optionstring)
|
||||
{
|
||||
int i;
|
||||
int enable;
|
||||
char *arg;
|
||||
|
||||
for ( arg = strtok(optionstring, ","); arg; arg = strtok(NULL, ",") ) {
|
||||
if ( *arg == '+' || *arg == '-' )
|
||||
enable = (*arg++ == '+') ? 1 : 0;
|
||||
else if ( strncasecmp(arg, "no", 2) == 0 ) {
|
||||
arg += 2;
|
||||
enable = 0;
|
||||
}
|
||||
else
|
||||
enable = 1;
|
||||
|
||||
for ( i=0; i < NR(opts); i++ )
|
||||
if ( strcasecmp(arg, opts[i].name) == 0 )
|
||||
break;
|
||||
|
||||
if ( i < NR(opts) ) {
|
||||
if ( opts[i].off )
|
||||
enable = !enable;
|
||||
|
||||
if ( enable )
|
||||
*flags |= opts[i].flag;
|
||||
else
|
||||
*flags &= ~opts[i].flag;
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "%s: unknown option <%s>\n", pgm, arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
e_flags(char *text, int size, char *context)
|
||||
{
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
float
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
int rc;
|
||||
int flags = 0;
|
||||
int debug = 0;
|
||||
int toc = 0;
|
||||
int use_mkd_line = 0;
|
||||
char *urlflags = 0;
|
||||
char *text = 0;
|
||||
char *ofile = 0;
|
||||
char *urlbase = 0;
|
||||
char *q;
|
||||
MMIOT *doc;
|
||||
|
||||
if ( q = getenv("MARKDOWN_FLAGS") )
|
||||
flags = strtol(q, 0, 0);
|
||||
|
||||
pgm = basename(argv[0]);
|
||||
opterr = 1;
|
||||
|
||||
while ( (opt=getopt(argc, argv, "b:df:F:o:s:t:TVZ:")) != EOF ) {
|
||||
switch (opt) {
|
||||
case 'b': urlbase = optarg;
|
||||
break;
|
||||
case 'd': debug = 1;
|
||||
break;
|
||||
case 'V': printf("%s: discount %s\n", pgm, markdown_version);
|
||||
exit(0);
|
||||
case 'F': flags = strtol(optarg, 0, 0);
|
||||
break;
|
||||
case 'f': set(&flags, optarg);
|
||||
break;
|
||||
case 't': text = optarg;
|
||||
use_mkd_line = 1;
|
||||
break;
|
||||
case 'T': toc = 1;
|
||||
break;
|
||||
case 's': text = optarg;
|
||||
break;
|
||||
case 'o': if ( ofile ) {
|
||||
fprintf(stderr, "Too many -o options\n");
|
||||
exit(1);
|
||||
}
|
||||
if ( !freopen(ofile = optarg, "w", stdout) ) {
|
||||
perror(ofile);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'Z': urlflags = optarg;
|
||||
break;
|
||||
default: fprintf(stderr, "usage: %s [-dTV] [-b url-base]"
|
||||
" [-F bitmap] [-f {+-}flags]"
|
||||
" [-o ofile] [-s text]"
|
||||
" [-t text] [file]\n", pgm);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if ( use_mkd_line )
|
||||
rc = mkd_generateline( text, strlen(text), stdout, flags);
|
||||
else {
|
||||
if ( text ) {
|
||||
if ( (doc = mkd_string(text, strlen(text), flags)) == 0 ) {
|
||||
perror(text);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( argc && !freopen(argv[0], "r", stdin) ) {
|
||||
perror(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
if ( (doc = mkd_in(stdin,flags)) == 0 ) {
|
||||
perror(argc ? argv[0] : "stdin");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if ( urlbase )
|
||||
mkd_basename(doc, urlbase);
|
||||
|
||||
if ( debug )
|
||||
rc = mkd_dump(doc, stdout, 0, argc ? basename(argv[0]) : "stdin");
|
||||
else {
|
||||
rc = 1;
|
||||
if ( mkd_compile(doc, flags) ) {
|
||||
if ( urlflags ) {
|
||||
mkd_e_context(doc, urlflags);
|
||||
mkd_e_flags(doc, e_flags);
|
||||
}
|
||||
rc = 0;
|
||||
if ( toc )
|
||||
mkd_generatetoc(doc, stdout);
|
||||
mkd_generatehtml(doc, stdout);
|
||||
mkd_cleanup(doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
adump();
|
||||
exit( (rc == 0) ? 0 : errno );
|
||||
}
|
||||
27
r2/r2/lib/contrib/discount-1.6.0/makepage.c
Normal file
27
r2/r2/lib/contrib/discount-1.6.0/makepage.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* makepage: Use mkd_xhtmlpage() to convert markdown input to a
|
||||
* fully-formed xhtml page.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mkdio.h>
|
||||
|
||||
float
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
MMIOT *doc;
|
||||
|
||||
if ( (argc > 1) && !freopen(argv[1], "r", stdin) ) {
|
||||
perror(argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( (doc = mkd_in(stdin, 0)) == 0 ) {
|
||||
perror( (argc > 1) ? argv[1] : "stdin" );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(mkd_xhtmlpage(doc, 0, stdout));
|
||||
}
|
||||
128
r2/r2/lib/contrib/discount-1.6.0/markdown.1
Normal file
128
r2/r2/lib/contrib/discount-1.6.0/markdown.1
Normal file
@@ -0,0 +1,128 @@
|
||||
.\" %A%
|
||||
.\"
|
||||
.Dd January 7, 2008
|
||||
.Dt MARKDOWN 1
|
||||
.Os MASTODON
|
||||
.Sh NAME
|
||||
.Nm markdown
|
||||
.Nd text to html conversion tool
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl d
|
||||
.Op Fl T
|
||||
.Op Fl V
|
||||
.Op Fl b Ar url-base
|
||||
.Op Fl F Pa bitmap
|
||||
.Op Fl f Ar flags
|
||||
.Op Fl o Pa file
|
||||
.Op Fl s Pa text
|
||||
.Op Fl t Pa text
|
||||
.Op Pa textfile
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility reads the
|
||||
.Xr markdown 7 Ns -formatted
|
||||
.Pa textfile
|
||||
.Pq or stdin if not specified,
|
||||
compiles it, and writes the html output
|
||||
to stdout.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "-o file"
|
||||
.It Fl b Ar url-base
|
||||
Links in source begining with / will be prefixed with
|
||||
.Ar url-base
|
||||
in the output.
|
||||
.It Fl d
|
||||
Instead of writing the html file, dump a parse
|
||||
tree to stdout.
|
||||
.It Fl f Ar flags
|
||||
Set or clear various translation flags. The flags
|
||||
are in a comma-delimited list, with an optional
|
||||
.Ar +
|
||||
(set) prefix on each flag.
|
||||
.Bl -tag -width "NOHEADER"
|
||||
.It Ar noimage
|
||||
Don't allow image tags.
|
||||
.It Ar nolinks
|
||||
Don't allow links.
|
||||
.It Ar nohtml
|
||||
Don't allow
|
||||
.B any
|
||||
embedded html.
|
||||
.It Ar cdata
|
||||
Generate valid XML output.
|
||||
.It Ar noheader
|
||||
Do not process pandoc headers.
|
||||
.It Ar notables
|
||||
Do not process Markdown Extra-style tables.
|
||||
.It Ar tabstops
|
||||
Use markdown-standard 4-space tabstops.
|
||||
.".It Ar strict
|
||||
."Disable superscript and relaxed emphasis.
|
||||
.".It Ar relax
|
||||
."Enable superscript and relaxed emphasis (this is the default.)
|
||||
.It Ar toc
|
||||
Enable table-of-contents support
|
||||
.It Ar 1.0
|
||||
Revert to Markdown 1.0 compatability.
|
||||
.El
|
||||
.Pp
|
||||
As an example, the option
|
||||
.Fl f Ar nolinks,quot
|
||||
tells
|
||||
.Nm
|
||||
to not allow \<a tags, and to expand
|
||||
double-quotes.
|
||||
.It Fl F Ar bitmap
|
||||
Set translation flags.
|
||||
.Ar Bitmap
|
||||
is a bit map of the various configuration options
|
||||
described in
|
||||
.Xr markdown 3
|
||||
(the flag values are defined in
|
||||
.Pa mkdio.h )
|
||||
.It Fl V
|
||||
Show the version# and configuration data.
|
||||
.Pp
|
||||
If the version includes the string
|
||||
.Em DL_TAG ,
|
||||
.Nm
|
||||
was configured with definition list support.
|
||||
.Pp
|
||||
If the version includes the string
|
||||
.Em HEADER ,
|
||||
.Nm
|
||||
was configured to support pandoc header blocks.
|
||||
.It Fl o Pa file
|
||||
Write the generated html to
|
||||
.Pa file .
|
||||
.It Fl t Ar text
|
||||
Use
|
||||
.Xr mkd_text 3
|
||||
to format
|
||||
.Ar text
|
||||
instead of processing stdin with the
|
||||
.Xr markdown 3
|
||||
function.
|
||||
.It Fl T
|
||||
If run with the table-of-content flag on, dump the
|
||||
table of contents before the formatted text.
|
||||
.It Fl s Ar text
|
||||
Use the
|
||||
.Xr markdown 3
|
||||
function to format
|
||||
.Ar text .
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Nm
|
||||
utility exits 0 on success, and >0 if an error occurs.
|
||||
.Sh SEE ALSO
|
||||
.Xr markdown 3 ,
|
||||
.Xr markdown 7 ,
|
||||
.Xr mkd-extensions 7 .
|
||||
.Sh AUTHOR
|
||||
.An David Parsons
|
||||
.Pq Li orc@pell.chi.il.us
|
||||
128
r2/r2/lib/contrib/discount-1.6.0/markdown.1.in
Normal file
128
r2/r2/lib/contrib/discount-1.6.0/markdown.1.in
Normal file
@@ -0,0 +1,128 @@
|
||||
.\" %A%
|
||||
.\"
|
||||
.Dd January 7, 2008
|
||||
.Dt MARKDOWN 1
|
||||
.Os MASTODON
|
||||
.Sh NAME
|
||||
.Nm markdown
|
||||
.Nd text to html conversion tool
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl d
|
||||
.Op Fl T
|
||||
.Op Fl V
|
||||
.Op Fl b Ar url-base
|
||||
.Op Fl F Pa bitmap
|
||||
.Op Fl f Ar flags
|
||||
.Op Fl o Pa file
|
||||
.Op Fl s Pa text
|
||||
.Op Fl t Pa text
|
||||
.Op Pa textfile
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility reads the
|
||||
.Xr markdown 7 Ns -formatted
|
||||
.Pa textfile
|
||||
.Pq or stdin if not specified,
|
||||
compiles it, and writes the html output
|
||||
to stdout.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width "-o file"
|
||||
.It Fl b Ar url-base
|
||||
Links in source begining with / will be prefixed with
|
||||
.Ar url-base
|
||||
in the output.
|
||||
.It Fl d
|
||||
Instead of writing the html file, dump a parse
|
||||
tree to stdout.
|
||||
.It Fl f Ar flags
|
||||
Set or clear various translation flags. The flags
|
||||
are in a comma-delimited list, with an optional
|
||||
.Ar +
|
||||
(set) prefix on each flag.
|
||||
.Bl -tag -width "NOHEADER"
|
||||
.It Ar noimage
|
||||
Don't allow image tags.
|
||||
.It Ar nolinks
|
||||
Don't allow links.
|
||||
.It Ar nohtml
|
||||
Don't allow
|
||||
.B any
|
||||
embedded html.
|
||||
.It Ar cdata
|
||||
Generate valid XML output.
|
||||
.It Ar noheader
|
||||
Do not process pandoc headers.
|
||||
.It Ar notables
|
||||
Do not process Markdown Extra-style tables.
|
||||
.It Ar tabstops
|
||||
Use markdown-standard 4-space tabstops.
|
||||
@STRICT@.It Ar strict
|
||||
@STRICT@Disable superscript and relaxed emphasis.
|
||||
@STRICT@.It Ar relax
|
||||
@STRICT@Enable superscript and relaxed emphasis (this is the default.)
|
||||
.It Ar toc
|
||||
Enable table-of-contents support
|
||||
.It Ar 1.0
|
||||
Revert to Markdown 1.0 compatability.
|
||||
.El
|
||||
.Pp
|
||||
As an example, the option
|
||||
.Fl f Ar nolinks,quot
|
||||
tells
|
||||
.Nm
|
||||
to not allow \<a tags, and to expand
|
||||
double-quotes.
|
||||
.It Fl F Ar bitmap
|
||||
Set translation flags.
|
||||
.Ar Bitmap
|
||||
is a bit map of the various configuration options
|
||||
described in
|
||||
.Xr markdown 3
|
||||
(the flag values are defined in
|
||||
.Pa mkdio.h )
|
||||
.It Fl V
|
||||
Show the version# and configuration data.
|
||||
.Pp
|
||||
If the version includes the string
|
||||
.Em DL_TAG ,
|
||||
.Nm
|
||||
was configured with definition list support.
|
||||
.Pp
|
||||
If the version includes the string
|
||||
.Em HEADER ,
|
||||
.Nm
|
||||
was configured to support pandoc header blocks.
|
||||
.It Fl o Pa file
|
||||
Write the generated html to
|
||||
.Pa file .
|
||||
.It Fl t Ar text
|
||||
Use
|
||||
.Xr mkd_text 3
|
||||
to format
|
||||
.Ar text
|
||||
instead of processing stdin with the
|
||||
.Xr markdown 3
|
||||
function.
|
||||
.It Fl T
|
||||
If run with the table-of-content flag on, dump the
|
||||
table of contents before the formatted text.
|
||||
.It Fl s Ar text
|
||||
Use the
|
||||
.Xr markdown 3
|
||||
function to format
|
||||
.Ar text .
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Nm
|
||||
utility exits 0 on success, and >0 if an error occurs.
|
||||
.Sh SEE ALSO
|
||||
.Xr markdown 3 ,
|
||||
.Xr markdown 7 ,
|
||||
.Xr mkd-extensions 7 .
|
||||
.Sh AUTHOR
|
||||
.An David Parsons
|
||||
.Pq Li orc@pell.chi.il.us
|
||||
123
r2/r2/lib/contrib/discount-1.6.0/markdown.3
Normal file
123
r2/r2/lib/contrib/discount-1.6.0/markdown.3
Normal file
@@ -0,0 +1,123 @@
|
||||
.\"
|
||||
.Dd December 20, 2007
|
||||
.Dt MARKDOWN 3
|
||||
.Os Mastodon
|
||||
.Sh NAME
|
||||
.Nm markdown
|
||||
.Nd process Markdown documents
|
||||
.Sh LIBRARY
|
||||
Markdown
|
||||
.Pq libmarkdown , -lmarkdown
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <mkdio.h>
|
||||
.Ft MMIOT
|
||||
.Fn *mkd_in "FILE *input" "int flags"
|
||||
.Ft MMIOT
|
||||
.Fn *mkd_string "char *string" "int size" "int flags"
|
||||
.Ft int
|
||||
.Fn markdown "MMIOT *doc" "FILE *output" "int flags"
|
||||
.Sh DESCRIPTION
|
||||
These functions
|
||||
convert
|
||||
.Em Markdown
|
||||
documents and strings into HTML.
|
||||
.Fn markdown
|
||||
processes an entire document, while
|
||||
.Fn mkd_text
|
||||
processes a single string.
|
||||
.Pp
|
||||
To process a file, you pass a FILE* to
|
||||
.Fn mkd_in ,
|
||||
and if it returns a nonzero value you pass that in to
|
||||
.Fn markdown ,
|
||||
which then writes the converted document to the specified
|
||||
.Em FILE* .
|
||||
If your input has already been written into a string (generated
|
||||
input or a file opened
|
||||
with
|
||||
.Xr mmap 2 )
|
||||
you can feed that string to
|
||||
.Fn mkd_string
|
||||
and pass its return value to
|
||||
.Fn markdown.
|
||||
.Pp
|
||||
.Fn Markdown
|
||||
accepts the following flag values (or-ed together if needed)
|
||||
to restrict how it processes input:
|
||||
.Bl -tag -width MKD_SAFELINK -compact
|
||||
.It Ar MKD_NOIMAGE
|
||||
Do not process `![]' and
|
||||
remove
|
||||
.Em \<img\>
|
||||
tags from the output.
|
||||
.It Ar MKD_NOLINKS
|
||||
Do not process `[]' and remove
|
||||
.Em \<a\>
|
||||
tags from the output.
|
||||
.It Ar MKD_NOPANTS
|
||||
Do not do Smartypants-style mangling of quotes, dashes, or ellipses.
|
||||
.It Ar MKD_STRICT
|
||||
Disable superscript and relaxed emphasis processing (if they are configured;
|
||||
otherwise it's a no-op.)
|
||||
.\" .It Ar MKD_QUOT
|
||||
.\" Expand
|
||||
.\" .Ar \&"
|
||||
.\" to \&".
|
||||
.It Ar MKD_NOHEADER
|
||||
Do not attempt to parse any Pandoc-style headers.
|
||||
.It Ar MKD_TABSTOP
|
||||
When reading documents, expand tabs to
|
||||
.Em 4
|
||||
spaces instead of whatever
|
||||
.Nm
|
||||
was originally configured for.
|
||||
.It Ar MKD_TOC
|
||||
Label all headers for use with the
|
||||
.Fn mkd_generatetoc
|
||||
and
|
||||
.Fn mkd_toc
|
||||
functions.
|
||||
.It Ar MKD_1_COMPAT
|
||||
MarkdownTest_1.0 compatability flag; trim trailing spaces from the
|
||||
first line of code blocks and disable implicit reference links.
|
||||
.It Ar MKD_AUTOLINK
|
||||
Greedily urlify links -- if
|
||||
.Em MKD_AUTOLINK
|
||||
is set, urls will be converted into hyperlinks even if they
|
||||
aren't encased in
|
||||
.Em <> .
|
||||
.It Ar MKD_SAFELINK
|
||||
Don't make hyperlinks from
|
||||
.Em [][]
|
||||
links that have unknown url types.
|
||||
.It Ar MKD_NOTABLES
|
||||
Don't process tables.
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
.Fn markdown
|
||||
returns 0 on success, 1 on failure.
|
||||
The
|
||||
.Fn mkd_in
|
||||
and
|
||||
.Fn mkd_string
|
||||
functions return a MMIOT* on success, null on failure.
|
||||
.Sh SEE ALSO
|
||||
.Xr markdown 1 ,
|
||||
.Xr mkd-functions 3 ,
|
||||
.Xr mkd-line 3 ,
|
||||
.Xr markdown 7 ,
|
||||
.Xr mkd-extensions 7 ,
|
||||
.Xr mmap 2 .
|
||||
.Pp
|
||||
http://daringfireball.net/projects/markdown/syntax
|
||||
.Sh BUGS
|
||||
Error handling is minimal at best.
|
||||
.Pp
|
||||
The
|
||||
.Ar MMIOT
|
||||
created by
|
||||
.Fn mkd_string
|
||||
is deleted by the
|
||||
.Nm
|
||||
function.
|
||||
|
||||
1019
r2/r2/lib/contrib/discount-1.6.0/markdown.7
Normal file
1019
r2/r2/lib/contrib/discount-1.6.0/markdown.7
Normal file
File diff suppressed because it is too large
Load Diff
1101
r2/r2/lib/contrib/discount-1.6.0/markdown.c
Normal file
1101
r2/r2/lib/contrib/discount-1.6.0/markdown.c
Normal file
File diff suppressed because it is too large
Load Diff
151
r2/r2/lib/contrib/discount-1.6.0/markdown.h
Normal file
151
r2/r2/lib/contrib/discount-1.6.0/markdown.h
Normal file
@@ -0,0 +1,151 @@
|
||||
#ifndef _MARKDOWN_D
|
||||
#define _MARKDOWN_D
|
||||
|
||||
#include "cstring.h"
|
||||
|
||||
/* reference-style links (and images) are stored in an array
|
||||
* of footnotes.
|
||||
*/
|
||||
typedef struct footnote {
|
||||
Cstring tag; /* the tag for the reference link */
|
||||
Cstring link; /* what this footnote points to */
|
||||
Cstring title; /* what it's called (TITLE= attribute) */
|
||||
int height, width; /* dimensions (for image link) */
|
||||
int dealloc; /* deallocation needed? */
|
||||
} Footnote;
|
||||
|
||||
/* each input line is read into a Line, which contains the line,
|
||||
* the offset of the first non-space character [this assumes
|
||||
* that all tabs will be expanded to spaces!], and a pointer to
|
||||
* the next line.
|
||||
*/
|
||||
typedef struct line {
|
||||
Cstring text;
|
||||
struct line *next;
|
||||
int dle;
|
||||
} Line;
|
||||
|
||||
|
||||
/* a paragraph is a collection of Lines, with links to the next paragraph
|
||||
* and (if it's a QUOTE, UL, or OL) to the reparsed contents of this
|
||||
* paragraph.
|
||||
*/
|
||||
typedef struct paragraph {
|
||||
struct paragraph *next; /* next paragraph */
|
||||
struct paragraph *down; /* recompiled contents of this paragraph */
|
||||
struct line *text; /* all the text in this paragraph */
|
||||
char *ident; /* %id% tag for QUOTE */
|
||||
enum { WHITESPACE=0, CODE, QUOTE, MARKUP,
|
||||
HTML, STYLE, DL, UL, OL, AL, LISTITEM,
|
||||
HDR, HR, TABLE, SOURCE } typ;
|
||||
enum { IMPLICIT=0, PARA, CENTER} align;
|
||||
int hnumber; /* <Hn> for typ == HDR */
|
||||
} Paragraph;
|
||||
|
||||
enum { ETX, SETEXT }; /* header types */
|
||||
|
||||
|
||||
typedef struct block {
|
||||
enum { bTEXT, bSTAR, bUNDER } b_type;
|
||||
int b_count;
|
||||
char b_char;
|
||||
Cstring b_text;
|
||||
Cstring b_post;
|
||||
} block;
|
||||
|
||||
typedef STRING(block) Qblock;
|
||||
|
||||
|
||||
/* a magic markdown io thing holds all the data structures needed to
|
||||
* do the backend processing of a markdown document
|
||||
*/
|
||||
typedef char* (*e_func)(const char*, const int, void*);
|
||||
|
||||
typedef struct mmiot {
|
||||
Cstring out;
|
||||
Cstring in;
|
||||
Qblock Q;
|
||||
int isp;
|
||||
STRING(Footnote) *footnotes;
|
||||
int flags;
|
||||
#define DENY_A 0x0001
|
||||
#define DENY_IMG 0x0002
|
||||
#define DENY_SMARTY 0x0004
|
||||
#define DENY_HTML 0x0008
|
||||
#define STRICT 0x0010
|
||||
#define INSIDE_TAG 0x0020
|
||||
#define NO_PSEUDO_PROTO 0x0040
|
||||
#define CDATA_OUTPUT 0x0080
|
||||
#define NOTABLES 0x0400
|
||||
#define TOC 0x1000
|
||||
#define MKD_1_COMPAT 0x2000
|
||||
#define AUTOLINK 0x4000
|
||||
#define SAFELINK 0x8000
|
||||
#define USER_FLAGS 0xFCFF
|
||||
#define EMBEDDED DENY_A|DENY_IMG|NO_PSEUDO_PROTO|CDATA_OUTPUT
|
||||
char *base;
|
||||
void *e_context;
|
||||
e_func e_url, e_flags;
|
||||
void (*e_free)(void*,void*);
|
||||
} MMIOT;
|
||||
|
||||
|
||||
/*
|
||||
* the mkdio text input functions return a document structure,
|
||||
* which contains a header (retrieved from the document if
|
||||
* markdown was configured * with the * --enable-pandoc-header
|
||||
* and the document begins with a pandoc-style header) and the
|
||||
* root of the linked list of Lines.
|
||||
*/
|
||||
typedef struct document {
|
||||
Line *headers; /* title -> author(s) -> date */
|
||||
ANCHOR(Line) content; /* uncompiled text, not valid after compile() */
|
||||
Paragraph *code; /* intermediate code generated by compile() */
|
||||
int compiled; /* set after mkd_compile() */
|
||||
int html; /* set after (internal) htmlify() */
|
||||
int tabstop; /* for properly expanding tabs (ick) */
|
||||
MMIOT *ctx; /* backend buffers, flags, and structures */
|
||||
char *base; /* url basename for url fragments */
|
||||
} Document;
|
||||
|
||||
|
||||
extern int mkd_firstnonblank(Line *);
|
||||
extern int mkd_compile(Document *, int);
|
||||
extern int mkd_document(Document *, char **);
|
||||
extern int mkd_generatehtml(Document *, FILE *);
|
||||
extern int mkd_css(Document *, char **);
|
||||
extern int mkd_generatecss(Document *, FILE *);
|
||||
#define mkd_style mkd_generatecss
|
||||
extern int mkd_xml(char *, int , char **);
|
||||
extern int mkd_generatexml(char *, int, FILE *);
|
||||
extern void mkd_cleanup(Document *);
|
||||
extern int mkd_line(char *, int, char **, int);
|
||||
extern int mkd_generateline(char *, int, FILE*, int);
|
||||
#define mkd_text mkd_generateline
|
||||
extern void mkd_basename(Document*, char *);
|
||||
extern void mkd_string_to_anchor(char*,int, void(*)(int,void*), void*);
|
||||
|
||||
extern Document *mkd_in(FILE *, int);
|
||||
extern Document *mkd_string(char*,int, int);
|
||||
|
||||
#define NO_HEADER 0x0100
|
||||
#define STD_TABSTOP 0x0200
|
||||
#define INPUT_MASK (NO_HEADER|STD_TABSTOP)
|
||||
|
||||
|
||||
/* internal resource handling functions.
|
||||
*/
|
||||
extern void ___mkd_freeLine(Line *);
|
||||
extern void ___mkd_freeLines(Line *);
|
||||
extern void ___mkd_freeParagraph(Paragraph *);
|
||||
extern void ___mkd_freefootnote(Footnote *);
|
||||
extern void ___mkd_freefootnotes(MMIOT *);
|
||||
extern void ___mkd_initmmiot(MMIOT *, void *);
|
||||
extern void ___mkd_freemmiot(MMIOT *, void *);
|
||||
extern void ___mkd_freeLineRange(Line *, Line *);
|
||||
extern void ___mkd_xml(char *, int, FILE *);
|
||||
extern void ___mkd_reparse(char *, int, int, MMIOT*);
|
||||
extern void ___mkd_emblock(MMIOT*);
|
||||
extern void ___mkd_tidy(Cstring *);
|
||||
|
||||
#endif/*_MARKDOWN_D*/
|
||||
178
r2/r2/lib/contrib/discount-1.6.0/mkd-extensions.7
Normal file
178
r2/r2/lib/contrib/discount-1.6.0/mkd-extensions.7
Normal file
@@ -0,0 +1,178 @@
|
||||
.\"
|
||||
.Dd Dec 22, 2007
|
||||
.Dt MKD-EXTENSIONS 7
|
||||
.Os MASTODON
|
||||
.Sh NAME
|
||||
.Nm mkd-extensions
|
||||
.Nd Extensions to the Markdown text formatting syntax
|
||||
.Sh DESCRIPTION
|
||||
This version of markdown has been extended in a few ways by
|
||||
extending existing markup, creating new markup from scratch,
|
||||
and borrowing markup from other markup languages.
|
||||
.Ss Image dimensions
|
||||
Markdown embedded images have been extended to allow specifying
|
||||
the dimensions of the image by adding a new argument
|
||||
.Em =/height/x/width/
|
||||
to the link description.
|
||||
.Pp
|
||||
The new image syntax is
|
||||
.nf
|
||||

|
||||
.fi
|
||||
.Ss pseudo-protocols
|
||||
Three pseudo-protocols have been added to links
|
||||
.Bl -tag -width XXXXX
|
||||
.It Ar id:
|
||||
The
|
||||
.Ar "alt text"
|
||||
is marked up and written to the output, wrapped with
|
||||
.Em "<a id=id>"
|
||||
and
|
||||
.Em "</a>" .
|
||||
.It Ar class:
|
||||
The
|
||||
.Ar "alt text"
|
||||
is marked up and written to the output, wrapped with
|
||||
.Em "<span class=class>"
|
||||
and
|
||||
.Em "</span>" .
|
||||
.It Ar raw:
|
||||
The
|
||||
.Ar title
|
||||
is written
|
||||
.Em -- with no further processing --
|
||||
to the output. The
|
||||
.Ar "alt text"
|
||||
is discarded.
|
||||
.It Ar abbr:
|
||||
The
|
||||
.Ar "alt text"
|
||||
is marked up and written to the output, wrapped with
|
||||
.Em "<abbr title="abbr">
|
||||
and
|
||||
.Em "</abbr>" .
|
||||
.El
|
||||
.Ss Pandoc headers
|
||||
If markdown was configured with
|
||||
.Ar --enable-pandoc-header ,
|
||||
the markdown source document can have a 3-line
|
||||
.Xr Pandoc
|
||||
header in the format of
|
||||
.nf
|
||||
% title
|
||||
% author(s)
|
||||
% date
|
||||
.fi
|
||||
which will be made available to the
|
||||
.Fn mkd_doc_title ,
|
||||
.Fn mkd_doc_author ,
|
||||
and
|
||||
.Fn mkd_doc_date
|
||||
functions.
|
||||
.Ss Definition lists
|
||||
If markdown was configured with
|
||||
.Ar --enable-dl-tag ,
|
||||
markup for definition lists is enabled. A definition list item
|
||||
is defined as
|
||||
.nf
|
||||
=tag=
|
||||
description
|
||||
.fi
|
||||
(that is a
|
||||
.Ar = ,
|
||||
followed by text, another
|
||||
.Ar = ,
|
||||
a newline, 4 spaces of intent, and then more text.)
|
||||
.Pp
|
||||
.Ss embedded stylesheets
|
||||
Stylesheets may be defined and modified in a
|
||||
.Em <style>
|
||||
block. A style block is parsed like any other
|
||||
block level html;
|
||||
.Em <style>
|
||||
starting on column 1, raw html (or, in this case, css) following
|
||||
it, and either ending with a
|
||||
.Em </style>
|
||||
at the end of the line or a
|
||||
.Em </style>
|
||||
at the beginning of a subsequent line.
|
||||
.Pp
|
||||
Be warned that style blocks work like footnote links -- no matter
|
||||
where you define them they are valid for the entire document.
|
||||
.Ss relaxed emphasis
|
||||
If markdown was configured with
|
||||
.Ar --relaxed-emphasis ,
|
||||
the rules for emphasis are changed so that a single
|
||||
.Ar _
|
||||
will
|
||||
.Em not
|
||||
count as a emphasis character if it's in the middle of a word.
|
||||
This is primarily for documenting code, if you don't wish to
|
||||
have to backquote all code references.
|
||||
.Ss alpha lists
|
||||
If markdown was configured with
|
||||
.Ar --enable-alpha-list ,
|
||||
alphabetic lists (like regular numeric lists, but with alphabetic
|
||||
items) are supported. So:
|
||||
.nf
|
||||
a. this
|
||||
b. is
|
||||
c. an alphabetic
|
||||
d. list
|
||||
.fi
|
||||
will produce:
|
||||
.nf
|
||||
<ol type=a>
|
||||
<li>this</li>
|
||||
<li>is</li>
|
||||
<li>an alphabetic</li>
|
||||
<li>list</li>
|
||||
</ol>
|
||||
.fi
|
||||
.Ss tables
|
||||
.Ar PHP Markdown Extra -style
|
||||
tables are supported; input of the form
|
||||
.nf
|
||||
header|header
|
||||
------|------
|
||||
text | text
|
||||
.fi
|
||||
will produce:
|
||||
.nf
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>header</th>
|
||||
<th>header</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>text</td>
|
||||
<td>text</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
.fi
|
||||
The dashed line can also contain
|
||||
.Em :
|
||||
characters for formatting; if a
|
||||
.Em :
|
||||
is at the start of a column, it tells
|
||||
.Nm discount
|
||||
to align the cell contents to the left; if it's at the end, it
|
||||
aligns right, and if there's one at the start and at the
|
||||
end, it centers.
|
||||
.Sh AUTHOR
|
||||
David Parsons
|
||||
.%T http://www.pell.portland.or.us/~orc/
|
||||
.Sh SEE ALSO
|
||||
.Xr markdown 1 ,
|
||||
.Xr markdown 3 ,
|
||||
.Xr mkd-functions 3 ,
|
||||
.Xr mkd-line 3 ,
|
||||
.Xr mkd-extensions 7 .
|
||||
.Pp
|
||||
.%T http://daringfireball.net/projects/markdown
|
||||
.Pp
|
||||
.%T http://michelf.com/projects/php-markdown
|
||||
180
r2/r2/lib/contrib/discount-1.6.0/mkd-functions.3
Normal file
180
r2/r2/lib/contrib/discount-1.6.0/mkd-functions.3
Normal file
@@ -0,0 +1,180 @@
|
||||
.\"
|
||||
.Dd January 18, 2008
|
||||
.Dt MKD_FUNCTIONS 3
|
||||
.Os Mastodon
|
||||
.Sh NAME
|
||||
.Nm mkd_functions
|
||||
.Nd access and process Markdown documents.
|
||||
.Sh LIBRARY
|
||||
Markdown
|
||||
.Pq libmarkdown , -lmarkdown
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <mkdio.h>
|
||||
.Ft int
|
||||
.Fn mkd_compile "MMIOT *document" "int flags"
|
||||
.Ft int
|
||||
.Fn mkd_css "MMIOT *document" "char **doc"
|
||||
.Ft int
|
||||
.Fn mkd_generatecss "MMIOT *document" "FILE *output"
|
||||
.Ft int
|
||||
.Fn mkd_document "MMIOT *document" "char **doc"
|
||||
.Ft int
|
||||
.Fn mkd_generatehtml "MMIOT *document" "FILE *output"
|
||||
.Ft int
|
||||
.Fn mkd_xhtmlpage "MMIOT *document" "int flags" "FILE *output"
|
||||
.Ft int
|
||||
.Fn mkd_toc "MMIOT *document" "char **doc"
|
||||
.Ft void
|
||||
.Fn mkd_generatetoc "MMIOT *document" "FILE *output"
|
||||
.Ft void
|
||||
.Fn mkd_cleanup "MMIOT*"
|
||||
.Ft char*
|
||||
.Fn mkd_doc_title "MMIOT*"
|
||||
.Ft char*
|
||||
.Fn mkd_doc_author "MMIOT*"
|
||||
.Ft char*
|
||||
.Fn mkd_doc_date "MMIOT*"
|
||||
.Sh DESCRIPTION
|
||||
.Pp
|
||||
The
|
||||
.Nm markdown
|
||||
format supported in this implementation includes
|
||||
Pandoc-style header and inline
|
||||
.Ar \<style\>
|
||||
blocks, and the standard
|
||||
.Xr markdown 3
|
||||
functions do not provide access to
|
||||
the data provided by either of those extensions.
|
||||
These functions give you access to that data, plus
|
||||
they provide a finer-grained way of converting
|
||||
.Em Markdown
|
||||
documents into HTML.
|
||||
.Pp
|
||||
Given a
|
||||
.Ar MMIOT*
|
||||
generated by
|
||||
.Fn mkd_in
|
||||
or
|
||||
.Fn mkd_string ,
|
||||
.Fn mkd_compile
|
||||
compiles the document into
|
||||
.Em \<style\> ,
|
||||
.Em Pandoc ,
|
||||
and
|
||||
.Em html
|
||||
sections.
|
||||
.Pp
|
||||
Once compiled, the document can be examined and written
|
||||
by the
|
||||
.Fn mkd_css ,
|
||||
.Fn mkd_document ,
|
||||
.Fn mkd_generatecss ,
|
||||
.Fn mkd_generatehtml ,
|
||||
.Fn mkd_generatetoc ,
|
||||
.Fn mkd_toc ,
|
||||
.Fn mkd_xhtmlpage ,
|
||||
.Fn mkd_doc_title ,
|
||||
.Fn mkd_doc_author ,
|
||||
and
|
||||
.Fn mkd_doc_date
|
||||
functions.
|
||||
.Pp
|
||||
.Fn mkd_css
|
||||
allocates a string and populates it with any \<style\> sections
|
||||
provided in the document,
|
||||
.Fn mkd_generatecss
|
||||
writes any \<style\> sections to the output,
|
||||
.Fn mkd_document
|
||||
points
|
||||
.Ar text
|
||||
to the text of the document and returns the
|
||||
size of the document,
|
||||
.Fn mkd_generatehtml
|
||||
writes the rest of the document to the output,
|
||||
and
|
||||
.Fn mkd_doc_title ,
|
||||
.Fn mkd_doc_author ,
|
||||
.Fn mkd_doc_date
|
||||
are used to read the contents of a Pandoc header,
|
||||
if any.
|
||||
.Pp
|
||||
.Fn mkd_xhtmlpage
|
||||
writes a xhtml page containing the document. The regular set of
|
||||
flags can be passed.
|
||||
.Pp
|
||||
.Fn mkd_toc
|
||||
writes a document outline, in the form of a collection of nested
|
||||
lists with links to each header in the document, into a string
|
||||
allocated with
|
||||
.Fn malloc ,
|
||||
and returns the size.
|
||||
.Pp
|
||||
.Fn mkd_generatetoc
|
||||
is like
|
||||
.Fn mkd_toc ,
|
||||
except that it writes the document outline to the given
|
||||
.Pa FILE*
|
||||
argument.
|
||||
.Pp
|
||||
.Fn mkd_cleanup
|
||||
deletes a
|
||||
.Ar MMIOT*
|
||||
after processing is done.
|
||||
.Pp
|
||||
.Fn mkd_compile
|
||||
accepts the same flags that
|
||||
.Fn markdown
|
||||
and
|
||||
.Fn mkd_string
|
||||
do;
|
||||
.Bl -tag -width MKD_NOIMAGE -compact
|
||||
.It Ar MKD_NOIMAGE
|
||||
Do not process `![]' and
|
||||
remove
|
||||
.Em \<img\>
|
||||
tags from the output.
|
||||
.It Ar MKD_NOLINKS
|
||||
Do not process `[]' and remove
|
||||
.Em \<a\>
|
||||
tags from the output.
|
||||
.It Ar MKD_NOPANTS
|
||||
Do not do Smartypants-style mangling of quotes, dashes, or ellipses.
|
||||
.It Ar MKD_TAGTEXT
|
||||
Process the input as if you were inside a html tag. This means that
|
||||
no html tags will be generated, and
|
||||
.Fn mkd_compile
|
||||
will attempt to escape anything that might terribly confuse a
|
||||
web browser.
|
||||
.It Ar MKD_NO_EXT
|
||||
Do not process any markdown pseudo-protocols when
|
||||
handing
|
||||
.Ar [][]
|
||||
links.
|
||||
.It Ar MKD_NOHEADER
|
||||
Do not attempt to parse any Pandoc-style headers.
|
||||
.It Ar MKD_TOC
|
||||
Label all headers for use with the
|
||||
.Fn mkd_generatetoc
|
||||
function.
|
||||
.It Ar MKD_1_COMPAT
|
||||
MarkdownTest_1.0 compatability flag; trim trailing spaces from the
|
||||
first line of code blocks and disable implicit reference links.
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
The functions
|
||||
.Fn mkd_compile ,
|
||||
.Fn mkd_style ,
|
||||
and
|
||||
.Fn mkd_generatehtml
|
||||
return 0 on success, -1 on failure.
|
||||
.Sh SEE ALSO
|
||||
.Xr markdown 1 ,
|
||||
.Xr markdown 3 ,
|
||||
.Xr mkd-line 3 ,
|
||||
.Xr markdown 7 ,
|
||||
.Xr mkd-extensions 7 ,
|
||||
.Xr mmap 2 .
|
||||
.Pp
|
||||
http://daringfireball.net/projects/markdown/syntax
|
||||
.Sh BUGS
|
||||
Error handling is minimal at best.
|
||||
41
r2/r2/lib/contrib/discount-1.6.0/mkd-line.3
Normal file
41
r2/r2/lib/contrib/discount-1.6.0/mkd-line.3
Normal file
@@ -0,0 +1,41 @@
|
||||
.\"
|
||||
.Dd January 18, 2008
|
||||
.Dt MKD_LINE 3
|
||||
.Os Mastodon
|
||||
.Sh NAME
|
||||
.Nm mkd_line
|
||||
.Nd do Markdown translation of small items
|
||||
.Sh LIBRARY
|
||||
Markdown
|
||||
.Pq libmarkdown , -lmarkdown
|
||||
.Sh SYNOPSIS
|
||||
.Fd #include <mkdio.h>
|
||||
.Ft int
|
||||
.Fn mkd_line "char *string" "int size" "char **doc" "int flags"
|
||||
.Ft int
|
||||
.Fn mkd_generateline "char *string" "int size" "FILE *output" "int flags"
|
||||
.Sh DESCRIPTION
|
||||
.Pp
|
||||
Occasionally one might want to do markdown translations on fragments of
|
||||
data, like the title of an weblog article, a date, or a simple signature
|
||||
line.
|
||||
.Nm mkd_line
|
||||
and
|
||||
.Nm mkd_generateline
|
||||
allow you to do markdown translations on small blocks of text.
|
||||
.Nm mkd_line
|
||||
allocates a buffer, then writes the translated text into that buffer,
|
||||
and
|
||||
.Nm mkd_generateline
|
||||
writes the output to the specified
|
||||
.Ar FILE* .
|
||||
.Sh SEE ALSO
|
||||
.Xr markdown 1 ,
|
||||
.Xr markdown 3 ,
|
||||
.Xr markdown 7 ,
|
||||
.Xr mkd-extensions 7 ,
|
||||
.Xr mmap 2 .
|
||||
.Pp
|
||||
http://daringfireball.net/projects/markdown/syntax
|
||||
.Sh BUGS
|
||||
Error handling is minimal at best.
|
||||
185
r2/r2/lib/contrib/discount-1.6.0/mkd2html.c
Normal file
185
r2/r2/lib/contrib/discount-1.6.0/mkd2html.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* mkd2html: parse a markdown input file and generate a web page.
|
||||
*
|
||||
* usage: mkd2html [options] filename
|
||||
* or mkd2html [options] < markdown > html
|
||||
*
|
||||
* options
|
||||
* -css css-file
|
||||
* -header line-to-add-to-<HEADER>
|
||||
* -footer line-to-add-before-</BODY>
|
||||
*
|
||||
* example:
|
||||
*
|
||||
* mkd2html -cs /~orc/pages.css syntax
|
||||
* ( read syntax OR syntax.text, write syntax.html )
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2007 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_BASENAME
|
||||
# ifdef HAVE_LIBGEN_H
|
||||
# include <libgen.h>
|
||||
# else
|
||||
# include <unistd.h>
|
||||
# endif
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "mkdio.h"
|
||||
#include "cstring.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
char *pgm = "mkd2html";
|
||||
|
||||
#ifndef HAVE_BASENAME
|
||||
char *
|
||||
basename(char *path)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (( p = strrchr(path, '/') ))
|
||||
return 1+p;
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
fail(char *why, ...)
|
||||
{
|
||||
va_list ptr;
|
||||
|
||||
va_start(ptr,why);
|
||||
fprintf(stderr, "%s: ", pgm);
|
||||
vfprintf(stderr, why, ptr);
|
||||
fputc('\n', stderr);
|
||||
va_end(ptr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
main(argc, argv)
|
||||
char **argv;
|
||||
{
|
||||
char *h;
|
||||
char *source = 0, *dest = 0;
|
||||
MMIOT *mmiot;
|
||||
int i;
|
||||
FILE *input, *output;
|
||||
STRING(char*) css, headers, footers;
|
||||
|
||||
|
||||
CREATE(css);
|
||||
CREATE(headers);
|
||||
CREATE(footers);
|
||||
pgm = basename(argv[0]);
|
||||
|
||||
while ( argc > 2 ) {
|
||||
if ( strcmp(argv[1], "-css") == 0 ) {
|
||||
EXPAND(css) = argv[2];
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
}
|
||||
else if ( strcmp(argv[1], "-header") == 0 ) {
|
||||
EXPAND(headers) = argv[2];
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
}
|
||||
else if ( strcmp(argv[1], "-footer") == 0 ) {
|
||||
EXPAND(footers) = argv[2];
|
||||
argc -= 2;
|
||||
argv += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( argc > 1 ) {
|
||||
char *p, *dot;
|
||||
|
||||
source = malloc(strlen(argv[1]) + 6);
|
||||
dest = malloc(strlen(argv[1]) + 6);
|
||||
|
||||
if ( !(source && dest) )
|
||||
fail("out of memory allocating name buffers");
|
||||
|
||||
strcpy(source, argv[1]);
|
||||
if (( p = strrchr(source, '/') ))
|
||||
p = source;
|
||||
else
|
||||
++p;
|
||||
|
||||
if ( (input = fopen(source, "r")) == 0 ) {
|
||||
strcat(source, ".text");
|
||||
if ( (input = fopen(source, "r")) == 0 )
|
||||
fail("can't open either %s or %s", argv[1], source);
|
||||
}
|
||||
strcpy(dest, source);
|
||||
|
||||
if (( dot = strrchr(dest, '.') ))
|
||||
*dot = 0;
|
||||
strcat(dest, ".html");
|
||||
|
||||
if ( (output = fopen(dest, "w")) == 0 )
|
||||
fail("can't write to %s", dest);
|
||||
}
|
||||
else {
|
||||
input = stdin;
|
||||
output = stdout;
|
||||
}
|
||||
|
||||
if ( (mmiot = mkd_in(input, 0)) == 0 )
|
||||
fail("can't read %s", source ? source : "stdin");
|
||||
|
||||
if ( !mkd_compile(mmiot, 0) )
|
||||
fail("couldn't compile input");
|
||||
|
||||
|
||||
h = mkd_doc_title(mmiot);
|
||||
|
||||
/* print a header */
|
||||
|
||||
fprintf(output,
|
||||
"<!doctype html public \"-//W3C//DTD HTML 4.0 Transitional //EN\">\n"
|
||||
"<html>\n"
|
||||
"<head>\n"
|
||||
" <meta name=\"GENERATOR\" content=\"mkd2html %s\">\n", markdown_version);
|
||||
|
||||
fprintf(output," <meta http-equiv=\"Content-Type\"\n"
|
||||
" content=\"text/html; charset-us-ascii\">");
|
||||
|
||||
for ( i=0; i < S(css); i++ )
|
||||
fprintf(output, " <link rel=\"stylesheet\"\n"
|
||||
" type=\"text/css\"\n"
|
||||
" href=\"%s\" />\n", T(css)[i]);
|
||||
|
||||
if ( h ) {
|
||||
fprintf(output," <title>");
|
||||
mkd_generateline(h, strlen(h), output, 0);
|
||||
fprintf(output, "</title>\n");
|
||||
}
|
||||
for ( i=0; i < S(headers); i++ )
|
||||
fprintf(output, " %s\n", T(headers)[i]);
|
||||
fprintf(output, "</head>\n"
|
||||
"<body>\n");
|
||||
|
||||
/* print the compiled body */
|
||||
|
||||
mkd_generatehtml(mmiot, output);
|
||||
|
||||
for ( i=0; i < S(footers); i++ )
|
||||
fprintf(output, "%s\n", T(footers)[i]);
|
||||
|
||||
fprintf(output, "</body>\n"
|
||||
"</html>\n");
|
||||
|
||||
mkd_cleanup(mmiot);
|
||||
exit(0);
|
||||
}
|
||||
339
r2/r2/lib/contrib/discount-1.6.0/mkdio.c
Normal file
339
r2/r2/lib/contrib/discount-1.6.0/mkdio.c
Normal file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* mkdio -- markdown front end input functions
|
||||
*
|
||||
* Copyright (C) 2007 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cstring.h"
|
||||
#include "markdown.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
typedef ANCHOR(Line) LineAnchor;
|
||||
|
||||
/* create a new blank Document
|
||||
*/
|
||||
static Document*
|
||||
new_Document()
|
||||
{
|
||||
Document *ret = calloc(sizeof(Document), 1);
|
||||
|
||||
if ( ret ) {
|
||||
if (( ret->ctx = calloc(sizeof(MMIOT), 1) ))
|
||||
return ret;
|
||||
free(ret);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* add a line to the markdown input chain
|
||||
*/
|
||||
static void
|
||||
queue(Document* a, Cstring *line)
|
||||
{
|
||||
Line *p = calloc(sizeof *p, 1);
|
||||
unsigned char c;
|
||||
int xp = 0;
|
||||
int size = S(*line);
|
||||
unsigned char *str = (unsigned char*)T(*line);
|
||||
|
||||
CREATE(p->text);
|
||||
ATTACH(a->content, p);
|
||||
|
||||
while ( size-- ) {
|
||||
if ( (c = *str++) == '\t' ) {
|
||||
/* expand tabs into ->tabstop spaces. We use ->tabstop
|
||||
* because the ENTIRE FREAKING COMPUTER WORLD uses editors
|
||||
* that don't do ^T/^D, but instead use tabs for indentation,
|
||||
* and, of course, set their tabs down to 4 spaces
|
||||
*/
|
||||
do {
|
||||
EXPAND(p->text) = ' ';
|
||||
} while ( ++xp % a->tabstop );
|
||||
}
|
||||
else if ( c >= ' ' ) {
|
||||
EXPAND(p->text) = c;
|
||||
++xp;
|
||||
}
|
||||
}
|
||||
EXPAND(p->text) = 0;
|
||||
S(p->text)--;
|
||||
p->dle = mkd_firstnonblank(p);
|
||||
}
|
||||
|
||||
|
||||
#ifdef PANDOC_HEADER
|
||||
/* trim leading blanks from a header line
|
||||
*/
|
||||
static void
|
||||
snip(Line *p)
|
||||
{
|
||||
CLIP(p->text, 0, 1);
|
||||
p->dle = mkd_firstnonblank(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* build a Document from any old input.
|
||||
*/
|
||||
typedef int (*getc_func)(void*);
|
||||
|
||||
Document *
|
||||
populate(getc_func getc, void* ctx, int flags)
|
||||
{
|
||||
Cstring line;
|
||||
Document *a = new_Document();
|
||||
int c;
|
||||
#ifdef PANDOC_HEADER
|
||||
int pandoc = 0;
|
||||
#endif
|
||||
|
||||
if ( !a ) return 0;
|
||||
|
||||
a->tabstop = (flags & STD_TABSTOP) ? 4 : TABSTOP;
|
||||
|
||||
CREATE(line);
|
||||
|
||||
while ( (c = (*getc)(ctx)) != EOF ) {
|
||||
if ( c == '\n' ) {
|
||||
#ifdef PANDOC_HEADER
|
||||
if ( pandoc != EOF && pandoc < 3 ) {
|
||||
if ( S(line) && (T(line)[0] == '%') )
|
||||
pandoc++;
|
||||
else
|
||||
pandoc = EOF;
|
||||
}
|
||||
#endif
|
||||
queue(a, &line);
|
||||
S(line) = 0;
|
||||
}
|
||||
else if ( isprint(c) || isspace(c) || (c & 0x80) )
|
||||
EXPAND(line) = c;
|
||||
}
|
||||
|
||||
if ( S(line) )
|
||||
queue(a, &line);
|
||||
|
||||
DELETE(line);
|
||||
|
||||
#ifdef PANDOC_HEADER
|
||||
if ( (pandoc == 3) && !(flags & NO_HEADER) ) {
|
||||
/* the first three lines started with %, so we have a header.
|
||||
* clip the first three lines out of content and hang them
|
||||
* off header.
|
||||
*/
|
||||
a->headers = T(a->content);
|
||||
T(a->content) = a->headers->next->next->next;
|
||||
a->headers->next->next->next = 0;
|
||||
snip(a->headers);
|
||||
snip(a->headers->next);
|
||||
snip(a->headers->next->next);
|
||||
}
|
||||
#endif
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/* convert a file into a linked list
|
||||
*/
|
||||
Document *
|
||||
mkd_in(FILE *f, int flags)
|
||||
{
|
||||
return populate((getc_func)fgetc, f, flags & INPUT_MASK);
|
||||
}
|
||||
|
||||
|
||||
/* return a single character out of a buffer
|
||||
*/
|
||||
struct string_ctx {
|
||||
char *data; /* the unread data */
|
||||
int size; /* and how much is there? */
|
||||
} ;
|
||||
|
||||
|
||||
static int
|
||||
strget(struct string_ctx *in)
|
||||
{
|
||||
if ( !in->size ) return EOF;
|
||||
|
||||
--(in->size);
|
||||
|
||||
return *(in->data)++;
|
||||
}
|
||||
|
||||
|
||||
/* convert a block of text into a linked list
|
||||
*/
|
||||
Document *
|
||||
mkd_string(char *buf, int len, int flags)
|
||||
{
|
||||
struct string_ctx about;
|
||||
|
||||
about.data = buf;
|
||||
about.size = len;
|
||||
|
||||
return populate((getc_func)strget, &about, flags & INPUT_MASK);
|
||||
}
|
||||
|
||||
|
||||
/* write the html to a file (xmlified if necessary)
|
||||
*/
|
||||
int
|
||||
mkd_generatehtml(Document *p, FILE *output)
|
||||
{
|
||||
char *doc;
|
||||
int szdoc;
|
||||
|
||||
if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
|
||||
if ( p->ctx->flags & CDATA_OUTPUT )
|
||||
mkd_generatexml(doc, szdoc, output);
|
||||
else
|
||||
fwrite(doc, szdoc, 1, output);
|
||||
putc('\n', output);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* convert some markdown text to html
|
||||
*/
|
||||
int
|
||||
markdown(Document *document, FILE *out, int flags)
|
||||
{
|
||||
if ( mkd_compile(document, flags) ) {
|
||||
mkd_generatehtml(document, out);
|
||||
mkd_cleanup(document);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mkd_basename(Document *document, char *base)
|
||||
{
|
||||
if ( document )
|
||||
document->base = base;
|
||||
}
|
||||
|
||||
|
||||
/* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
|
||||
*/
|
||||
void
|
||||
mkd_string_to_anchor(char *s, int len, void(*outchar)(int,void*), void *out)
|
||||
{
|
||||
unsigned char c;
|
||||
|
||||
for ( ; len-- > 0; ) {
|
||||
c = *s++;
|
||||
if ( c == ' ' || c == '&' || c == '<' || c == '"' )
|
||||
(*outchar)('+', out);
|
||||
else if ( isalnum(c) || ispunct(c) || (c & 0x80) )
|
||||
(*outchar)(c, out);
|
||||
else
|
||||
(*outchar)('~',out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ___mkd_reparse() a line
|
||||
*/
|
||||
static void
|
||||
mkd_parse_line(char *bfr, int size, MMIOT *f, int flags)
|
||||
{
|
||||
___mkd_initmmiot(f, 0);
|
||||
f->flags = flags & USER_FLAGS;
|
||||
___mkd_reparse(bfr, size, 0, f);
|
||||
___mkd_emblock(f);
|
||||
}
|
||||
|
||||
|
||||
/* ___mkd_reparse() a line, returning it in malloc()ed memory
|
||||
*/
|
||||
int
|
||||
mkd_line(char *bfr, int size, char **res, int flags)
|
||||
{
|
||||
MMIOT f;
|
||||
int len;
|
||||
|
||||
mkd_parse_line(bfr, size, &f, flags);
|
||||
|
||||
if ( len = S(f.out) ) {
|
||||
/* kludge alert; we know that T(f.out) is malloced memory,
|
||||
* so we can just steal it away. This is awful -- there
|
||||
* should be an opaque method that transparently moves
|
||||
* the pointer out of the embedded Cstring.
|
||||
*/
|
||||
*res = T(f.out);
|
||||
T(f.out) = 0;
|
||||
S(f.out) = 0;
|
||||
}
|
||||
else {
|
||||
*res = 0;
|
||||
len = EOF;
|
||||
}
|
||||
___mkd_freemmiot(&f, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* ___mkd_reparse() a line, writing it to a FILE
|
||||
*/
|
||||
int
|
||||
mkd_generateline(char *bfr, int size, FILE *output, int flags)
|
||||
{
|
||||
MMIOT f;
|
||||
|
||||
mkd_parse_line(bfr, size, &f, flags);
|
||||
if ( flags & CDATA_OUTPUT )
|
||||
mkd_generatexml(T(f.out), S(f.out), output);
|
||||
else
|
||||
fwrite(T(f.out), S(f.out), 1, output);
|
||||
|
||||
___mkd_freemmiot(&f, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* set the url display callback
|
||||
*/
|
||||
void
|
||||
mkd_e_url(Document *f, e_func edit)
|
||||
{
|
||||
if ( f && f->ctx ) f->ctx->e_url = edit;
|
||||
}
|
||||
|
||||
|
||||
/* set the url options callback
|
||||
*/
|
||||
void
|
||||
mkd_e_flags(Document *f, e_func edit)
|
||||
{
|
||||
if ( f && f->ctx ) f->ctx->e_flags = edit;
|
||||
}
|
||||
|
||||
|
||||
/* set the url display/options deallocator
|
||||
*/
|
||||
void
|
||||
mkd_e_free(Document *f, void (*dealloc)(void*,void*))
|
||||
{
|
||||
if ( f && f->ctx ) f->ctx->e_free = dealloc;
|
||||
}
|
||||
|
||||
|
||||
/* set the url display/options context field
|
||||
*/
|
||||
void
|
||||
mkd_e_context(Document *f, void *context)
|
||||
{
|
||||
if ( f && f->ctx ) f->ctx->e_context = context;
|
||||
}
|
||||
87
r2/r2/lib/contrib/discount-1.6.0/mkdio.h
Normal file
87
r2/r2/lib/contrib/discount-1.6.0/mkdio.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef _MKDIO_D
|
||||
#define _MKDIO_D
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void MMIOT;
|
||||
|
||||
/* line builder for markdown()
|
||||
*/
|
||||
MMIOT *mkd_in(FILE*,int); /* assemble input from a file */
|
||||
MMIOT *mkd_string(char*,int,int); /* assemble input from a buffer */
|
||||
|
||||
void mkd_basename(MMIOT*,char*);
|
||||
|
||||
/* compilation, debugging, cleanup
|
||||
*/
|
||||
int mkd_compile(MMIOT*, int);
|
||||
int mkd_cleanup(MMIOT*);
|
||||
|
||||
/* markup functions
|
||||
*/
|
||||
int mkd_dump(MMIOT*, FILE*, int, char*);
|
||||
int markdown(MMIOT*, FILE*, int);
|
||||
int mkd_line(char *, int, char **, int);
|
||||
void mkd_string_to_anchor(char *, int, int (*)(int,void*), void*);
|
||||
int mkd_xhtmlpage(MMIOT*,int,FILE*);
|
||||
|
||||
/* header block access
|
||||
*/
|
||||
char* mkd_doc_title(MMIOT*);
|
||||
char* mkd_doc_author(MMIOT*);
|
||||
char* mkd_doc_date(MMIOT*);
|
||||
|
||||
/* compiled data access
|
||||
*/
|
||||
int mkd_document(MMIOT*, char**);
|
||||
int mkd_toc(MMIOT*, char**);
|
||||
int mkd_css(MMIOT*, char **);
|
||||
int mkd_xml(char *, int, char **);
|
||||
|
||||
/* write-to-file functions
|
||||
*/
|
||||
int mkd_generatehtml(MMIOT*,FILE*);
|
||||
int mkd_generatetoc(MMIOT*,FILE*);
|
||||
int mkd_generatexml(char *, int,FILE*);
|
||||
int mkd_generatecss(MMIOT*,FILE*);
|
||||
#define mkd_style mkd_generatecss
|
||||
int mkd_generateline(char *, int, FILE*, int);
|
||||
#define mkd_text mkd_generateline
|
||||
|
||||
/* url generator callbacks
|
||||
*/
|
||||
typedef char * (e_func)(char*, int, void*);
|
||||
void mkd_e_url(void *, e_func);
|
||||
void mkd_e_flags(void *, e_func);
|
||||
void mkd_e_free(void *, void (*dealloc)(void*, void*) );
|
||||
void mkd_e_context(void *, void *);
|
||||
|
||||
/* version#.
|
||||
*/
|
||||
extern char markdown_version[];
|
||||
|
||||
/* special flags for markdown() and mkd_text()
|
||||
*/
|
||||
#define MKD_NOLINKS 0x0001 /* don't do link processing, block <a> tags */
|
||||
#define MKD_NOIMAGE 0x0002 /* don't do image processing, block <img> */
|
||||
#define MKD_NOPANTS 0x0004 /* don't run smartypants() */
|
||||
#define MKD_NOHTML 0x0008 /* don't allow raw html through AT ALL */
|
||||
#define MKD_STRICT 0x0010 /* disable SUPERSCRIPT, RELAXED_EMPHASIS */
|
||||
#define MKD_TAGTEXT 0x0020 /* process text inside an html tag; no
|
||||
* <em>, no <bold>, no html or [] expansion */
|
||||
#define MKD_NO_EXT 0x0040 /* don't allow pseudo-protocols */
|
||||
#define MKD_CDATA 0x0080 /* generate code for xml ![CDATA[...]] */
|
||||
#define MKD_NOTABLES 0x0400 /* disallow tables */
|
||||
#define MKD_TOC 0x1000 /* do table-of-contents processing */
|
||||
#define MKD_1_COMPAT 0x2000 /* compatability with MarkdownTest_1.0 */
|
||||
#define MKD_AUTOLINK 0x4000 /* make http://foo.com link even without <>s */
|
||||
#define MKD_SAFELINK 0x8000 /* paranoid check for link protocol */
|
||||
#define MKD_EMBED MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
|
||||
|
||||
/* special flags for mkd_in() and mkd_string()
|
||||
*/
|
||||
#define MKD_NOHEADER 0x0100 /* don't process header blocks */
|
||||
#define MKD_TABSTOP 0x0200 /* expand tabs to 4 spaces */
|
||||
|
||||
|
||||
#endif/*_MKDIO_D*/
|
||||
155
r2/r2/lib/contrib/discount-1.6.0/resource.c
Normal file
155
r2/r2/lib/contrib/discount-1.6.0/resource.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/* markdown: a C implementation of John Gruber's Markdown markup language.
|
||||
*
|
||||
* Copyright (C) 2007 David L Parsons.
|
||||
* The redistribution terms are provided in the COPYRIGHT file that must
|
||||
* be distributed with this source code.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "cstring.h"
|
||||
#include "markdown.h"
|
||||
#include "amalloc.h"
|
||||
|
||||
/* free a (single) line
|
||||
*/
|
||||
void
|
||||
___mkd_freeLine(Line *ptr)
|
||||
{
|
||||
DELETE(ptr->text);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
|
||||
/* free a list of lines
|
||||
*/
|
||||
void
|
||||
___mkd_freeLines(Line *p)
|
||||
{
|
||||
if (p->next)
|
||||
___mkd_freeLines(p->next);
|
||||
___mkd_freeLine(p);
|
||||
}
|
||||
|
||||
|
||||
/* bye bye paragraph.
|
||||
*/
|
||||
void
|
||||
___mkd_freeParagraph(Paragraph *p)
|
||||
{
|
||||
if (p->next)
|
||||
___mkd_freeParagraph(p->next);
|
||||
if (p->down)
|
||||
___mkd_freeParagraph(p->down);
|
||||
if (p->text)
|
||||
___mkd_freeLines(p->text);
|
||||
if (p->ident)
|
||||
free(p->ident);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
||||
/* bye bye footnote.
|
||||
*/
|
||||
void
|
||||
___mkd_freefootnote(Footnote *f)
|
||||
{
|
||||
DELETE(f->tag);
|
||||
DELETE(f->link);
|
||||
DELETE(f->title);
|
||||
}
|
||||
|
||||
|
||||
/* bye bye footnotes.
|
||||
*/
|
||||
void
|
||||
___mkd_freefootnotes(MMIOT *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( f->footnotes ) {
|
||||
for (i=0; i < S(*f->footnotes); i++)
|
||||
___mkd_freefootnote( &T(*f->footnotes)[i] );
|
||||
DELETE(*f->footnotes);
|
||||
free(f->footnotes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* initialize a new MMIOT
|
||||
*/
|
||||
void
|
||||
___mkd_initmmiot(MMIOT *f, void *footnotes)
|
||||
{
|
||||
if ( f ) {
|
||||
memset(f, 0, sizeof *f);
|
||||
CREATE(f->in);
|
||||
CREATE(f->out);
|
||||
CREATE(f->Q);
|
||||
if ( footnotes )
|
||||
f->footnotes = footnotes;
|
||||
else {
|
||||
f->footnotes = malloc(sizeof f->footnotes[0]);
|
||||
CREATE(*f->footnotes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* free the contents of a MMIOT, but leave the object alone.
|
||||
*/
|
||||
void
|
||||
___mkd_freemmiot(MMIOT *f, void *footnotes)
|
||||
{
|
||||
if ( f ) {
|
||||
DELETE(f->in);
|
||||
DELETE(f->out);
|
||||
DELETE(f->Q);
|
||||
if ( f->footnotes != footnotes )
|
||||
___mkd_freefootnotes(f);
|
||||
memset(f, 0, sizeof *f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* free lines up to an barrier.
|
||||
*/
|
||||
void
|
||||
___mkd_freeLineRange(Line *anchor, Line *stop)
|
||||
{
|
||||
Line *r = anchor->next;
|
||||
|
||||
if ( r != stop ) {
|
||||
while ( r && (r->next != stop) )
|
||||
r = r->next;
|
||||
if ( r ) r->next = 0;
|
||||
___mkd_freeLines(anchor->next);
|
||||
}
|
||||
anchor->next = 0;
|
||||
}
|
||||
|
||||
|
||||
/* clean up everything allocated in __mkd_compile()
|
||||
*/
|
||||
void
|
||||
mkd_cleanup(Document *doc)
|
||||
{
|
||||
if ( doc ) {
|
||||
if ( doc->ctx ) {
|
||||
___mkd_freemmiot(doc->ctx, 0);
|
||||
free(doc->ctx);
|
||||
}
|
||||
|
||||
if ( doc->code) ___mkd_freeParagraph(doc->code);
|
||||
if ( doc->headers ) ___mkd_freeLines(doc->headers);
|
||||
if ( T(doc->content) ) ___mkd_freeLines(T(doc->content));
|
||||
memset(doc, 0, sizeof doc[0]);
|
||||
free(doc);
|
||||
}
|
||||
}
|
||||
46
r2/r2/lib/contrib/discount-1.6.0/tests/autolink.t
Normal file
46
r2/r2/lib/contrib/discount-1.6.0/tests/autolink.t
Normal file
@@ -0,0 +1,46 @@
|
||||
./echo 'Reddit-style automatic links'
|
||||
rc=0
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try -fautolink 'single link' \
|
||||
'http://www.pell.portland.or.us/~orc/Code/discount' \
|
||||
'<p><a href="http://www.pell.portland.or.us/~orc/Code/discount">http://www.pell.portland.or.us/~orc/Code/discount</a></p>'
|
||||
|
||||
try -fautolink '[!](http://a.com "http://b.com")' \
|
||||
'[!](http://a.com "http://b.com")' \
|
||||
'<p><a href="http://a.com" title="http://b.com">!</a></p>'
|
||||
|
||||
try -fautolink 'link surrounded by text' \
|
||||
'here http://it is?' \
|
||||
'<p>here <a href="http://it">http://it</a> is?</p>'
|
||||
|
||||
try -fautolink 'naked @' '@' '<p>@</p>'
|
||||
|
||||
try -fautolink 'parenthesised (url)' \
|
||||
'(http://here)' \
|
||||
'<p>(<a href="http://here">http://here</a>)</p>'
|
||||
|
||||
try -fautolink 'token with trailing @' 'orc@' '<p>orc@</p>'
|
||||
|
||||
exit $rc
|
||||
53
r2/r2/lib/contrib/discount-1.6.0/tests/automatic.t
Normal file
53
r2/r2/lib/contrib/discount-1.6.0/tests/automatic.t
Normal file
@@ -0,0 +1,53 @@
|
||||
./echo "automatic links"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
match() {
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
if ./echo "$2" | ./markdown | grep "$3" >/dev/null; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'http url' '<http://here>' '<p><a href="http://here">http://here</a></p>'
|
||||
try 'ftp url' '<ftp://here>' '<p><a href="ftp://here">ftp://here</a></p>'
|
||||
match '<orc@pell.portland.or.us>' '<orc@pell.portland.or.us>' '<a href='
|
||||
match '<orc@pell.com.>' '<orc@pell.com.>' '<a href='
|
||||
try 'invalid <orc@>' '<orc@>' '<p><orc@></p>'
|
||||
try 'invalid <@pell>' '<@pell>' '<p><@pell></p>'
|
||||
try 'invalid <orc@pell>' '<orc@pell>' '<p><orc@pell></p>'
|
||||
try 'invalid <orc@.pell>' '<orc@.pell>' '<p><orc@.pell></p>'
|
||||
try 'invalid <orc@pell.>' '<orc@pell.>' '<p><orc@pell.></p>'
|
||||
match '<mailto:orc@pell>' '<mailto:orc@pell>' '<a href='
|
||||
match '<mailto:orc@pell.com>' '<mailto:orc@pell.com>' '<a href='
|
||||
match '<mailto:orc@>' '<mailto:orc@>' '<a href='
|
||||
match '<mailto:@pell>' '<mailto:@pell>' '<a href='
|
||||
|
||||
exit $rc
|
||||
35
r2/r2/lib/contrib/discount-1.6.0/tests/backslash.t
Normal file
35
r2/r2/lib/contrib/discount-1.6.0/tests/backslash.t
Normal file
@@ -0,0 +1,35 @@
|
||||
./echo "backslash escapes"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got: $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'backslashes in []()' '[foo](http://\this\is\.a\test\(here\))' \
|
||||
'<p><a href="http://\this\is.a\test(here)">foo</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with trailing \' \
|
||||
'http://a.com/\' \
|
||||
'<p><a href="http://a.com/\">http://a.com/\</a></p>'
|
||||
|
||||
|
||||
exit $rc
|
||||
13
r2/r2/lib/contrib/discount-1.6.0/tests/chrome.text
Normal file
13
r2/r2/lib/contrib/discount-1.6.0/tests/chrome.text
Normal file
@@ -0,0 +1,13 @@
|
||||
->###chrome with my markdown###<-
|
||||
|
||||
1. `(c)` -> `©` (c)
|
||||
2. `(r)` -> `®` (r)
|
||||
3. `(tm)` -> `™` (tm)
|
||||
4. `...` -> `…` ...
|
||||
5. `--` -> `&emdash;` --
|
||||
6. `-` -> `–` - (but not if it's between-words)
|
||||
7. "fancy quoting"
|
||||
8. 'fancy quoting (#2)'
|
||||
9. don't do it unless it's a real quote.
|
||||
10. `` (`) ``
|
||||
|
||||
39
r2/r2/lib/contrib/discount-1.6.0/tests/code.t
Normal file
39
r2/r2/lib/contrib/discount-1.6.0/tests/code.t
Normal file
@@ -0,0 +1,39 @@
|
||||
./echo "code blocks"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'format for code block html' \
|
||||
' this is
|
||||
code' \
|
||||
'<pre><code>this is
|
||||
code
|
||||
</code></pre>'
|
||||
|
||||
try 'unclosed single backtick' '`hi there' '<p>`hi there</p>'
|
||||
try 'unclosed double backtick' '``hi there' '<p>``hi there</p>'
|
||||
try 'remove space around code' '`` hi there ``' '<p><code>hi there</code></p>'
|
||||
|
||||
exit $rc
|
||||
47
r2/r2/lib/contrib/discount-1.6.0/tests/compat.t
Normal file
47
r2/r2/lib/contrib/discount-1.6.0/tests/compat.t
Normal file
@@ -0,0 +1,47 @@
|
||||
./echo "markdown 1.0 compatability"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
LINKY='[this] is a test
|
||||
|
||||
[this]: /this'
|
||||
|
||||
try 'implicit reference links' "$LINKY" '<p><a href="/this">this</a> is a test</p>'
|
||||
try -f1.0 'implicit reference links (-f1.0)' "$LINKY" '<p>[this] is a test</p>'
|
||||
|
||||
WSP=' '
|
||||
WHITESPACE="
|
||||
white space$WSP
|
||||
and more"
|
||||
|
||||
try 'trailing whitespace' "$WHITESPACE" '<pre><code>white space ''
|
||||
and more
|
||||
</code></pre>'
|
||||
|
||||
try -f1.0 'trailing whitespace (-f1.0)' "$WHITESPACE" '<pre><code>white space''
|
||||
and more
|
||||
</code></pre>'
|
||||
|
||||
exit $rc
|
||||
57
r2/r2/lib/contrib/discount-1.6.0/tests/crash.t
Normal file
57
r2/r2/lib/contrib/discount-1.6.0/tests/crash.t
Normal file
@@ -0,0 +1,57 @@
|
||||
./echo "crashes"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
./echo -n ' zero-length input ................ '
|
||||
|
||||
if ./markdown < /dev/null >/dev/null; then
|
||||
./echo "ok"
|
||||
else
|
||||
./echo "FAILED"
|
||||
rc=1
|
||||
fi
|
||||
|
||||
./echo -n ' hanging quote in list ............ '
|
||||
|
||||
./markdown >/dev/null 2>/dev/null << EOF
|
||||
* > this should not die
|
||||
|
||||
no.
|
||||
EOF
|
||||
|
||||
if [ "$?" -eq 0 ]; then
|
||||
./echo "ok"
|
||||
else
|
||||
./echo "FAILED"
|
||||
rc=1
|
||||
fi
|
||||
|
||||
./echo -n ' dangling list item ............... '
|
||||
|
||||
if ./echo ' - ' | ./markdown >/dev/null 2>/dev/null; then
|
||||
./echo "ok"
|
||||
else
|
||||
./echo "FAILED"
|
||||
rc=1
|
||||
fi
|
||||
|
||||
./echo -n ' empty []() with baseurl .......... '
|
||||
|
||||
if ./markdown -bHOHO -s '[]()' >/dev/null 2>/dev/null; then
|
||||
./echo "ok"
|
||||
else
|
||||
./echo "FAILED"
|
||||
rc=1
|
||||
fi
|
||||
|
||||
./echo -n ' unclosed html block .............. '
|
||||
|
||||
if ./echo '<table></table' | ./markdown >/dev/null 2>/dev/null; then
|
||||
./echo 'ok'
|
||||
else
|
||||
./echo "FAILED"
|
||||
rc=1
|
||||
fi
|
||||
|
||||
exit $rc
|
||||
67
r2/r2/lib/contrib/discount-1.6.0/tests/div.t
Normal file
67
r2/r2/lib/contrib/discount-1.6.0/tests/div.t
Normal file
@@ -0,0 +1,67 @@
|
||||
./markdown -V | grep DIV >/dev/null || exit 0
|
||||
|
||||
./echo "%div% blocks"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'simple >%div% block' \
|
||||
'>%this%
|
||||
this this' \
|
||||
'<div class="this"><p>this this</p></div>'
|
||||
|
||||
try 'two >%div% blocks in a row' \
|
||||
'>%this%
|
||||
this this
|
||||
|
||||
>%that%
|
||||
that that' \
|
||||
'<div class="this"><p>this this</p></div>
|
||||
|
||||
<div class="that"><p>that that</p></div>'
|
||||
|
||||
try '>%class:div%' \
|
||||
'>%class:this%
|
||||
this this' \
|
||||
'<div class="this"><p>this this</p></div>'
|
||||
|
||||
try '>%id:div%' \
|
||||
'>%id:this%
|
||||
this this' \
|
||||
'<div id="this"><p>this this</p></div>'
|
||||
|
||||
try 'nested >%div%' \
|
||||
'>%this%
|
||||
>>%that%
|
||||
>>that
|
||||
|
||||
>%more%
|
||||
more' \
|
||||
'<div class="this"><div class="that"><p>that</p></div></div>
|
||||
|
||||
<div class="more"><p>more</p></div>'
|
||||
|
||||
|
||||
exit $rc
|
||||
69
r2/r2/lib/contrib/discount-1.6.0/tests/dl.t
Normal file
69
r2/r2/lib/contrib/discount-1.6.0/tests/dl.t
Normal file
@@ -0,0 +1,69 @@
|
||||
./echo "definition lists"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
SRC='
|
||||
=this=
|
||||
is an ugly
|
||||
=test=
|
||||
eh?'
|
||||
|
||||
RSLT='<dl>
|
||||
<dt>this</dt>
|
||||
<dd>is an ugly</dd>
|
||||
<dt>test</dt>
|
||||
<dd>eh?</dd>
|
||||
</dl>'
|
||||
|
||||
if ./markdown -V | grep DL_TAG >/dev/null; then
|
||||
|
||||
try '=tag= generates definition lists' "$SRC" "$RSLT"
|
||||
|
||||
try 'one item with two =tags=' \
|
||||
'=this=
|
||||
=is=
|
||||
A test, eh?' \
|
||||
'<dl>
|
||||
<dt>this</dt>
|
||||
<dt>is</dt>
|
||||
<dd>A test, eh?</dd>
|
||||
</dl>'
|
||||
|
||||
|
||||
else
|
||||
try '=tag= does nothing' "$SRC" \
|
||||
'<p>=this=</p>
|
||||
|
||||
<pre><code>is an ugly
|
||||
</code></pre>
|
||||
|
||||
<p>=test=</p>
|
||||
|
||||
<pre><code>eh?
|
||||
</code></pre>'
|
||||
|
||||
fi
|
||||
|
||||
exit $rc
|
||||
9
r2/r2/lib/contrib/discount-1.6.0/tests/embedlinks.text
Normal file
9
r2/r2/lib/contrib/discount-1.6.0/tests/embedlinks.text
Normal file
@@ -0,0 +1,9 @@
|
||||
* [] (http://dustmite.org)
|
||||
* [[an embedded link](http://wontwork.org)](http://willwork.org)
|
||||
* [![dustmite][]] (http:/dustmite.org)
|
||||
* ![dustmite][]
|
||||
* ![dustmite][dustmite]
|
||||
* [<a href="http://cheating.us">cheat me</a>](http://I.am.cheating)
|
||||
|
||||
[dustmite]: http://dustmite.org/mite.jpg =25x25 "here I am!"
|
||||
|
||||
40
r2/r2/lib/contrib/discount-1.6.0/tests/emphasis.t
Normal file
40
r2/r2/lib/contrib/discount-1.6.0/tests/emphasis.t
Normal file
@@ -0,0 +1,40 @@
|
||||
./echo "emphasis"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
try '*hi* -> <em>hi</em>' '*hi*' '<p><em>hi</em></p>'
|
||||
try '* -> *' 'A * A' '<p>A * A</p>'
|
||||
try -fstrict '***A**B*' '***A**B*' '<p><em><strong>A</strong>B</em></p>'
|
||||
try -fstrict '***A*B**' '***A*B**' '<p><strong><em>A</em>B</strong></p>'
|
||||
try -fstrict '**A*B***' '**A*B***' '<p><strong>A<em>B</em></strong></p>'
|
||||
try -fstrict '*A**B***' '*A**B***' '<p><em>A<strong>B</strong></em></p>'
|
||||
|
||||
if ./markdown -V | grep RELAXED >/dev/null; then
|
||||
try -frelax '_A_B with -frelax' '_A_B' '<p>_A_B</p>'
|
||||
try -fstrict '_A_B with -fstrict' '_A_B' '<p><em>A</em>B</p>'
|
||||
fi
|
||||
|
||||
exit $rc
|
||||
52
r2/r2/lib/contrib/discount-1.6.0/tests/flow.t
Normal file
52
r2/r2/lib/contrib/discount-1.6.0/tests/flow.t
Normal file
@@ -0,0 +1,52 @@
|
||||
./echo "paragraph flow"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'header followed by paragraph' \
|
||||
'###Hello, sailor###
|
||||
And how are you today?' \
|
||||
'<h3>Hello, sailor</h3>
|
||||
|
||||
<p>And how are you today?</p>'
|
||||
|
||||
try 'two lists punctuated with a HR' \
|
||||
'* A
|
||||
* * *
|
||||
* B
|
||||
* C' \
|
||||
'<ul>
|
||||
<li>A</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<ul>
|
||||
<li>B</li>
|
||||
<li>C</li>
|
||||
</ul>'
|
||||
|
||||
exit $rc
|
||||
34
r2/r2/lib/contrib/discount-1.6.0/tests/footnotes.t
Normal file
34
r2/r2/lib/contrib/discount-1.6.0/tests/footnotes.t
Normal file
@@ -0,0 +1,34 @@
|
||||
./echo "footnotes"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'a line with multiple []s' '[a][] [b][]:' '<p>[a][] [b][]:</p>'
|
||||
try 'a valid footnote' \
|
||||
'[alink][]
|
||||
|
||||
[alink]: link_me' \
|
||||
'<p><a href="link_me">alink</a></p>'
|
||||
|
||||
exit $rc
|
||||
46
r2/r2/lib/contrib/discount-1.6.0/tests/header.t
Normal file
46
r2/r2/lib/contrib/discount-1.6.0/tests/header.t
Normal file
@@ -0,0 +1,46 @@
|
||||
./echo "headers"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
S=`./echo -n "$1" '..................................' | ./cols 34`
|
||||
./echo -n " $S "
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo "ok"
|
||||
else
|
||||
./echo "FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'single #' '#' '<p>#</p>'
|
||||
try 'empty ETX' '##' '<h1>#</h1>'
|
||||
try 'single-char ETX (##W)' '##W' '<h2>W</h2>'
|
||||
try 'single-char ETX (##W )' '##W ' '<h2>W</h2>'
|
||||
try 'single-char ETX (## W)' '## W' '<h2>W</h2>'
|
||||
try 'single-char ETX (## W )' '## W ' '<h2>W</h2>'
|
||||
try 'single-char ETX (##W##)' '##W##' '<h2>W</h2>'
|
||||
try 'single-char ETX (##W ##)' '##W ##' '<h2>W</h2>'
|
||||
try 'single-char ETX (## W##)' '## W##' '<h2>W</h2>'
|
||||
try 'single-char ETX (## W ##)' '## W ##' '<h2>W</h2>'
|
||||
|
||||
try 'multiple-char ETX (##Hello##)' '##Hello##' '<h2>Hello</h2>'
|
||||
|
||||
try 'SETEXT with trailing whitespace' \
|
||||
'hello
|
||||
===== ' '<h1>hello</h1>'
|
||||
|
||||
exit $rc
|
||||
112
r2/r2/lib/contrib/discount-1.6.0/tests/html.t
Normal file
112
r2/r2/lib/contrib/discount-1.6.0/tests/html.t
Normal file
@@ -0,0 +1,112 @@
|
||||
./echo "html blocks"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'self-closing block tags (hr)' \
|
||||
'<hr>
|
||||
|
||||
text' \
|
||||
'<hr>
|
||||
|
||||
|
||||
<p>text</p>'
|
||||
|
||||
try 'self-closing block tags (hr/)' \
|
||||
'<hr/>
|
||||
|
||||
text' \
|
||||
'<hr/>
|
||||
|
||||
|
||||
<p>text</p>'
|
||||
|
||||
try 'self-closing block tags (br)' \
|
||||
'<br>
|
||||
|
||||
text' \
|
||||
'<br>
|
||||
|
||||
|
||||
<p>text</p>'
|
||||
|
||||
try 'html comments' \
|
||||
'<!--
|
||||
**hi**
|
||||
-->' \
|
||||
'<!--
|
||||
**hi**
|
||||
-->'
|
||||
|
||||
try 'no smartypants inside tags (#1)' \
|
||||
'<img src="linky">' \
|
||||
'<p><img src="linky"></p>'
|
||||
|
||||
try 'no smartypants inside tags (#2)' \
|
||||
'<img src="linky" alt=":)" />' \
|
||||
'<p><img src="linky" alt=":)" /></p>'
|
||||
|
||||
try -fnohtml 'block html with -fnohtml' '<b>hi!</b>' '<p><b>hi!</b></p>'
|
||||
try -fnohtml 'malformed tag injection' '<x <script>' '<p><x <script></p>'
|
||||
try -fhtml 'allow html with -fhtml' '<b>hi!</b>' '<p><b>hi!</b></p>'
|
||||
|
||||
|
||||
# check that nested raw html blocks terminate properly.
|
||||
#
|
||||
BLOCK1SRC='Markdown works fine *here*.
|
||||
|
||||
*And* here.
|
||||
|
||||
<div><pre>
|
||||
</pre></div>
|
||||
|
||||
Markdown here is *not* parsed by RDiscount.
|
||||
|
||||
Nor in *this* paragraph, and there are no paragraph breaks.'
|
||||
|
||||
BLOCK1OUT='<p>Markdown works fine <em>here</em>.</p>
|
||||
|
||||
<p><em>And</em> here.</p>
|
||||
|
||||
<div><pre>
|
||||
</pre></div>
|
||||
|
||||
|
||||
<p>Markdown here is <em>not</em> parsed by RDiscount.</p>
|
||||
|
||||
<p>Nor in <em>this</em> paragraph, and there are no paragraph breaks.</p>'
|
||||
|
||||
try 'nested html blocks (1)' "$BLOCK1SRC" "$BLOCK1OUT"
|
||||
|
||||
try 'nested html blocks (2)' \
|
||||
'<div>This is inside a html block
|
||||
<div>This is, too</div>and
|
||||
so is this</div>' \
|
||||
'<div>This is inside a html block
|
||||
<div>This is, too</div>and
|
||||
so is this</div>'
|
||||
|
||||
try 'unfinished tags' '<foo bar' '<p><foo bar</p>'
|
||||
|
||||
exit $rc
|
||||
14
r2/r2/lib/contrib/discount-1.6.0/tests/links.text
Normal file
14
r2/r2/lib/contrib/discount-1.6.0/tests/links.text
Normal file
@@ -0,0 +1,14 @@
|
||||
1. <http://automatic>
|
||||
2. [automatic] (http://automatic "automatic link")
|
||||
3. [automatic](http://automatic "automatic link")
|
||||
4. [automatic](http://automatic)
|
||||
5. [automatic] (http://automatic)
|
||||
6. [automatic] []
|
||||
7. [automatic][]
|
||||
8. [my][automatic]
|
||||
9. [my] [automatic]
|
||||
|
||||
[automatic]: http://automatic "footnote"
|
||||
|
||||
|
||||
[automatic] [
|
||||
124
r2/r2/lib/contrib/discount-1.6.0/tests/linkylinky.t
Normal file
124
r2/r2/lib/contrib/discount-1.6.0/tests/linkylinky.t
Normal file
@@ -0,0 +1,124 @@
|
||||
./echo "embedded links"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'url contains &' '[hehehe](u&rl)' '<p><a href="u&rl">hehehe</a></p>'
|
||||
try 'url contains +' '[hehehe](u+rl)' '<p><a href="u+rl">hehehe</a></p>'
|
||||
try 'url contains "' '[hehehe](u"rl)' '<p><a href="u%22rl">hehehe</a></p>'
|
||||
try 'url contains <' '[hehehe](u<rl)' '<p><a href="u<rl">hehehe</a></p>'
|
||||
try 'url contains whitespace' '[ha](r u)' '<p><a href="r%20u">ha</a></p>'
|
||||
|
||||
try 'url contains whitespace & title' \
|
||||
'[hehehe](r u "there")' \
|
||||
'<p><a href="r%20u" title="there">hehehe</a></p>'
|
||||
|
||||
try 'url contains escaped )' \
|
||||
'[hehehe](u\))' \
|
||||
'<p><a href="u)">hehehe</a></p>'
|
||||
|
||||
try 'image label contains <' \
|
||||
'' \
|
||||
'<p><img src="url" alt="he<he<he" /></p>'
|
||||
|
||||
try 'image label contains >' \
|
||||
'' \
|
||||
'<p><img src="url" alt="he>he>he" /></p>'
|
||||
|
||||
try 'sloppy context link' \
|
||||
'[heh]( url "how about it?" )' \
|
||||
'<p><a href="url" title="how about it?">heh</a></p>'
|
||||
|
||||
try 'footnote urls formed properly' \
|
||||
'[hehehe]: hohoho "ha ha"
|
||||
|
||||
[hehehe][]' \
|
||||
'<p><a href="hohoho" title="ha ha">hehehe</a></p>'
|
||||
|
||||
try 'linky-like []s work' \
|
||||
'[foo]' \
|
||||
'<p>[foo]</p>'
|
||||
|
||||
try 'pseudo-protocol "id:"'\
|
||||
'[foo](id:bar)' \
|
||||
'<p><a id="bar">foo</a></p>'
|
||||
|
||||
try 'pseudo-protocol "class:"' \
|
||||
'[foo](class:bar)' \
|
||||
'<p><span class="bar">foo</span></p>'
|
||||
|
||||
try 'pseudo-protocol "abbr:"'\
|
||||
'[foo](abbr:bar)' \
|
||||
'<p><abbr title="bar">foo</abbr></p>'
|
||||
|
||||
try 'nested [][]s' \
|
||||
'[[z](y)](x)' \
|
||||
'<p><a href="x">[z](y)</a></p>'
|
||||
|
||||
try 'empty [][] tags' \
|
||||
'[![][1]][2]
|
||||
|
||||
[1]: image1
|
||||
[2]: image2' \
|
||||
'<p><a href="image2"><img src="image1" alt="" /></a></p>'
|
||||
|
||||
try 'footnote cuddled up to text' \
|
||||
'foo
|
||||
[bar]:bar' \
|
||||
'<p>foo</p>'
|
||||
|
||||
try 'mid-paragraph footnote' \
|
||||
'talk talk talk talk
|
||||
[bar]: bar
|
||||
talk talk talk talk' \
|
||||
'<p>talk talk talk talk
|
||||
talk talk talk talk</p>'
|
||||
|
||||
try 'mid-blockquote footnote' \
|
||||
'>blockquote!
|
||||
[footnote]: here!
|
||||
>blockquote!' \
|
||||
'<blockquote><p>blockquote!
|
||||
blockquote!</p></blockquote>'
|
||||
|
||||
try 'end-blockquote footnote' \
|
||||
'>blockquote!
|
||||
>blockquote!
|
||||
[footnote]: here!' \
|
||||
'<blockquote><p>blockquote!
|
||||
blockquote!</p></blockquote>'
|
||||
|
||||
try 'start-blockquote footnote' \
|
||||
'[footnote]: here!
|
||||
>blockquote!
|
||||
>blockquote!' \
|
||||
'<blockquote><p>blockquote!
|
||||
blockquote!</p></blockquote>'
|
||||
|
||||
try '[text] (text) not a link' \
|
||||
'[test] (me)' \
|
||||
'<p>[test] (me)</p>'
|
||||
|
||||
exit $rc
|
||||
31
r2/r2/lib/contrib/discount-1.6.0/tests/linkypix.t
Normal file
31
r2/r2/lib/contrib/discount-1.6.0/tests/linkypix.t
Normal file
@@ -0,0 +1,31 @@
|
||||
./echo "embedded images"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'image with size extension' \
|
||||
'' \
|
||||
'<p><img src="pic" height="200" width="200" alt="picture" /></p>'
|
||||
|
||||
exit $rc
|
||||
178
r2/r2/lib/contrib/discount-1.6.0/tests/list.t
Normal file
178
r2/r2/lib/contrib/discount-1.6.0/tests/list.t
Normal file
@@ -0,0 +1,178 @@
|
||||
./echo "lists"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'two separated items' \
|
||||
' * A
|
||||
|
||||
* B' \
|
||||
'<ul>
|
||||
<li><p>A</p></li>
|
||||
<li><p>B</p></li>
|
||||
</ul>'
|
||||
|
||||
try 'two adjacent items' \
|
||||
' * A
|
||||
* B' \
|
||||
'<ul>
|
||||
<li>A</li>
|
||||
<li>B</li>
|
||||
</ul>'
|
||||
|
||||
|
||||
try 'two adjacent items, then space' \
|
||||
' * A
|
||||
* B
|
||||
|
||||
space, the final frontier' \
|
||||
'<ul>
|
||||
<li>A</li>
|
||||
<li>B</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>space, the final frontier</p>'
|
||||
|
||||
try 'nested lists (1)' \
|
||||
' * 1. Sub (list)
|
||||
2. Two (items)
|
||||
3. Here' \
|
||||
'<ul>
|
||||
<li><ol>
|
||||
<li>Sub (list)</li>
|
||||
<li>Two (items)</li>
|
||||
<li>Here</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ul>'
|
||||
|
||||
try 'nested lists (2)' \
|
||||
' * A (list)
|
||||
|
||||
1. Sub (list)
|
||||
2. Two (items)
|
||||
3. Here
|
||||
|
||||
Here
|
||||
* B (list)' \
|
||||
'<ul>
|
||||
<li><p>A (list)</p>
|
||||
|
||||
<ol>
|
||||
<li>Sub (list)</li>
|
||||
<li>Two (items)</li>
|
||||
<li>Here</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<p> Here</p></li>
|
||||
<li>B (list)</li>
|
||||
</ul>'
|
||||
|
||||
try 'list inside blockquote' \
|
||||
'>A (list)
|
||||
>
|
||||
>1. Sub (list)
|
||||
>2. Two (items)
|
||||
>3. Here' \
|
||||
'<blockquote><p>A (list)</p>
|
||||
|
||||
<ol>
|
||||
<li>Sub (list)</li>
|
||||
<li>Two (items)</li>
|
||||
<li>Here</li>
|
||||
</ol>
|
||||
</blockquote>'
|
||||
|
||||
try 'blockquote inside list' \
|
||||
' * A (list)
|
||||
|
||||
> quote
|
||||
> me
|
||||
|
||||
dont quote me' \
|
||||
'<ul>
|
||||
<li><p>A (list)</p>
|
||||
|
||||
<blockquote><p>quote
|
||||
me</p></blockquote>
|
||||
|
||||
<p>dont quote me</p></li>
|
||||
</ul>'
|
||||
|
||||
try 'empty list' \
|
||||
'
|
||||
-
|
||||
|
||||
-
|
||||
' \
|
||||
'<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>'
|
||||
|
||||
|
||||
try 'blockquote inside a list' \
|
||||
' * This is a list item.
|
||||
|
||||
> This is a quote insde a list item. ' \
|
||||
'<ul>
|
||||
<li><p> This is a list item.</p>
|
||||
|
||||
<blockquote><p>This is a quote insde a list item.</p></blockquote></li>
|
||||
</ul>'
|
||||
|
||||
if ./markdown -V | grep DL_TAG >/dev/null; then
|
||||
|
||||
try 'dl followed by non-dl' \
|
||||
'=a=
|
||||
test
|
||||
2. here' \
|
||||
'<dl>
|
||||
<dt>a</dt>
|
||||
<dd>test</dd>
|
||||
</dl>
|
||||
|
||||
<ol>
|
||||
<li>here</li>
|
||||
</ol>'
|
||||
|
||||
try 'non-dl followed by dl' \
|
||||
'1. hello
|
||||
=sailor=
|
||||
hi!' \
|
||||
'<ol>
|
||||
<li>hello</li>
|
||||
</ol>
|
||||
|
||||
|
||||
<dl>
|
||||
<dt>sailor</dt>
|
||||
<dd>hi!</dd>
|
||||
</dl>'
|
||||
|
||||
fi
|
||||
|
||||
exit $rc
|
||||
57
r2/r2/lib/contrib/discount-1.6.0/tests/list3deep.t
Normal file
57
r2/r2/lib/contrib/discount-1.6.0/tests/list3deep.t
Normal file
@@ -0,0 +1,57 @@
|
||||
./echo "deeply nested lists"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
LIST='
|
||||
* top-level list ( list 1)
|
||||
+ second-level list (list 2)
|
||||
* first item third-level list (list 3)
|
||||
+ * second item, third-level list, first item. (list 4)
|
||||
* second item, third-level list, second item.
|
||||
* top-level list again.'
|
||||
|
||||
RSLT='<ul>
|
||||
<li>top-level list ( list 1)
|
||||
|
||||
<ul>
|
||||
<li>second-level list (list 2)
|
||||
|
||||
<ul>
|
||||
<li>first item third-level list (list 3)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><ul>
|
||||
<li>second item, third-level list, first item. (list 4)</li>
|
||||
<li>second item, third-level list, second item.</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>top-level list again.</li>
|
||||
</ul>'
|
||||
|
||||
try 'thrice-nested lists' "$LIST" "$RSLT"
|
||||
|
||||
exit $rc
|
||||
33
r2/r2/lib/contrib/discount-1.6.0/tests/misc.t
Normal file
33
r2/r2/lib/contrib/discount-1.6.0/tests/misc.t
Normal file
@@ -0,0 +1,33 @@
|
||||
./echo "misc"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'single paragraph' 'AAA' '<p>AAA</p>'
|
||||
try '< -> <' '<' '<p><</p>'
|
||||
try '`>` -> <code>></code>' '`>`' '<p><code>></code></p>'
|
||||
try '`` ` `` -> <code>`</code>' '`` ` ``' '<p><code>`</code></p>'
|
||||
|
||||
exit $rc
|
||||
74
r2/r2/lib/contrib/discount-1.6.0/tests/pandoc.t
Normal file
74
r2/r2/lib/contrib/discount-1.6.0/tests/pandoc.t
Normal file
@@ -0,0 +1,74 @@
|
||||
./echo "pandoc headers"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
HEADER='% title
|
||||
% author(s)
|
||||
% date'
|
||||
|
||||
|
||||
if ./markdown -V | grep HEADER > /dev/null; then
|
||||
|
||||
try 'valid header' "$HEADER" ''
|
||||
try -F0x0100 'valid header with -F0x0100' "$HEADER" '<p>% title
|
||||
% author(s)
|
||||
% date</p>'
|
||||
|
||||
try 'invalid header' \
|
||||
'% title
|
||||
% author(s)
|
||||
a pony!' \
|
||||
'<p>% title
|
||||
% author(s)
|
||||
a pony!</p>'
|
||||
|
||||
try 'offset header' \
|
||||
'
|
||||
% title
|
||||
% author(s)
|
||||
% date' \
|
||||
'<p>% title
|
||||
% author(s)
|
||||
% date</p>'
|
||||
|
||||
try 'indented header' \
|
||||
' % title
|
||||
% author(s)
|
||||
% date' \
|
||||
'<p> % title
|
||||
% author(s)
|
||||
% date</p>'
|
||||
|
||||
else
|
||||
|
||||
try 'ignore headers' "$HEADER" '<p>% title
|
||||
% author(s)
|
||||
% date</p>'
|
||||
|
||||
fi
|
||||
|
||||
exit $rc
|
||||
38
r2/r2/lib/contrib/discount-1.6.0/tests/para.t
Normal file
38
r2/r2/lib/contrib/discount-1.6.0/tests/para.t
Normal file
@@ -0,0 +1,38 @@
|
||||
./echo "paragraph blocking"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'paragraph followed by code' \
|
||||
'a
|
||||
b' \
|
||||
'<p>a</p>
|
||||
|
||||
<pre><code>b
|
||||
</code></pre>'
|
||||
|
||||
try 'single-line paragraph' 'a' '<p>a</p>'
|
||||
|
||||
exit $rc
|
||||
31
r2/r2/lib/contrib/discount-1.6.0/tests/paranoia.t
Normal file
31
r2/r2/lib/contrib/discount-1.6.0/tests/paranoia.t
Normal file
@@ -0,0 +1,31 @@
|
||||
./echo "paranoia"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try -fsafelink 'bogus url (-fsafelink)' '[test](bad:protocol)' '<p>[test](bad:protocol)</p>'
|
||||
try -fnosafelink 'bogus url (-fnosafelink)' '[test](bad:protocol)' '<p><a href="bad:protocol">test</a></p>'
|
||||
|
||||
exit $rc
|
||||
66
r2/r2/lib/contrib/discount-1.6.0/tests/peculiarities.t
Normal file
66
r2/r2/lib/contrib/discount-1.6.0/tests/peculiarities.t
Normal file
@@ -0,0 +1,66 @@
|
||||
./echo "markup peculiarities"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try 'list followed by header .......... ' \
|
||||
"
|
||||
- AAA
|
||||
- BBB
|
||||
-" \
|
||||
'<ul>
|
||||
<li>AAA
|
||||
|
||||
<h2>– BBB</h2></li>
|
||||
</ul>'
|
||||
|
||||
try 'ul with mixed item prefixes' \
|
||||
'
|
||||
- A
|
||||
1. B' \
|
||||
'<ul>
|
||||
<li>A</li>
|
||||
<li>B</li>
|
||||
</ul>'
|
||||
|
||||
try 'ol with mixed item prefixes' \
|
||||
'
|
||||
1. A
|
||||
- B
|
||||
' \
|
||||
'<ol>
|
||||
<li>A</li>
|
||||
<li>B</li>
|
||||
</ol>'
|
||||
|
||||
try 'forcing a <br/>' 'this
|
||||
is' '<p>this<br/>
|
||||
is</p>'
|
||||
|
||||
try 'trimming single spaces' 'this ' '<p>this</p>'
|
||||
try -fnohtml 'markdown <br/> with -fnohtml' 'foo
|
||||
is' '<p>foo<br/>
|
||||
is</p>'
|
||||
|
||||
exit $rc
|
||||
35
r2/r2/lib/contrib/discount-1.6.0/tests/pseudo.t
Normal file
35
r2/r2/lib/contrib/discount-1.6.0/tests/pseudo.t
Normal file
@@ -0,0 +1,35 @@
|
||||
./echo "pseudo-protocols"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try '[](id:) links' '[foo](id:bar)' '<p><a id="bar">foo</a></p>'
|
||||
try -fnoext '[](id:) links with -fnoext' '[foo](id:bar)' '<p>[foo](id:bar)</p>'
|
||||
try '[](class:) links' '[foo](class:bar)' '<p><span class="bar">foo</span></p>'
|
||||
try -fnoext '[](class:) links with -fnoext' '[foo](class:bar)' '<p>[foo](class:bar)</p>'
|
||||
try '[](raw:) links' '[foo](raw:bar)' '<p>bar</p>'
|
||||
try -fnoext '[](raw:) links with -fnoext' '[foo](raw:bar)' '<p>[foo](raw:bar)</p>'
|
||||
|
||||
exit $rc
|
||||
34
r2/r2/lib/contrib/discount-1.6.0/tests/reparse.t
Normal file
34
r2/r2/lib/contrib/discount-1.6.0/tests/reparse.t
Normal file
@@ -0,0 +1,34 @@
|
||||
./echo "footnotes inside reparse sections"
|
||||
|
||||
rc=0
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
./echo -n " $1" '..................................' | ./cols 36
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo " ok"
|
||||
else
|
||||
./echo " FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
try 'footnote inside [] section' \
|
||||
'[![foo][]](bar)
|
||||
|
||||
[foo]: bar2' \
|
||||
'<p><a href="bar"><img src="bar2" alt="foo" /></a></p>'
|
||||
|
||||
exit $rc
|
||||
104
r2/r2/lib/contrib/discount-1.6.0/tests/schiraldi.t
Normal file
104
r2/r2/lib/contrib/discount-1.6.0/tests/schiraldi.t
Normal file
@@ -0,0 +1,104 @@
|
||||
./echo "Bugs & misfeatures reported by Mike Schiraldi"
|
||||
|
||||
rc=0
|
||||
MARKDOWN_FLAGS=
|
||||
|
||||
try() {
|
||||
unset FLAGS
|
||||
case "$1" in
|
||||
-*) FLAGS=$1
|
||||
shift ;;
|
||||
esac
|
||||
|
||||
S=`./echo -n "$1" '..................................' | ./cols 34`
|
||||
./echo -n " $S "
|
||||
|
||||
Q=`./echo "$2" | ./markdown $FLAGS`
|
||||
|
||||
|
||||
if [ "$3" = "$Q" ]; then
|
||||
./echo "ok"
|
||||
else
|
||||
./echo "FAILED"
|
||||
./echo "wanted: $3"
|
||||
./echo "got : $Q"
|
||||
rc=1
|
||||
fi
|
||||
}
|
||||
|
||||
try -fnohtml 'breaks with -fnohtml' 'foo
|
||||
bar' '<p>foo<br/>
|
||||
bar</p>'
|
||||
|
||||
try 'links with trailing \)' \
|
||||
'[foo](http://en.wikipedia.org/wiki/Link_(film\))' \
|
||||
'<p><a href="http://en.wikipedia.org/wiki/Link_(film)">foo</a></p>'
|
||||
|
||||
try -fautolink '(url) with -fautolink' \
|
||||
'(http://tsfr.org)' \
|
||||
'<p>(<a href="http://tsfr.org">http://tsfr.org</a>)</p>'
|
||||
|
||||
try 'single #' \
|
||||
'#' \
|
||||
'<p>#</p>'
|
||||
|
||||
try -frelax '* processing with -frelax' \
|
||||
'2*4 = 8 * 1 = 2**3' \
|
||||
'<p>2*4 = 8 * 1 = 2**3</p>'
|
||||
|
||||
try -fnopants '[]() with a single quote mark' \
|
||||
'[Poe'"'"'s law](http://rationalwiki.com/wiki/Poe'"'"'s_Law)' \
|
||||
'<p><a href="http://rationalwiki.com/wiki/Poe'"'"'s_Law">Poe'"'"'s law</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with escaped spaces' \
|
||||
'http://\(here\ I\ am\)' \
|
||||
'<p><a href="http://(here%20I%20am)">http://(here I am)</a></p>'
|
||||
|
||||
try -fautolink 'autolink café_racer' \
|
||||
'http://en.wikipedia.org/wiki/café_racer' \
|
||||
'<p><a href="http://en.wikipedia.org/wiki/caf%C3%A9_racer">http://en.wikipedia.org/wiki/caf%C3%A9_racer</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with arguments' \
|
||||
'http://foo.bar?a&b=c' \
|
||||
'<p><a href="http://foo.bar?a&b=c">http://foo.bar?a&b=c</a></p>'
|
||||
|
||||
try '\( escapes in []()' \
|
||||
'[foo](http://a.com/\(foo\))' \
|
||||
'<p><a href="http://a.com/(foo)">foo</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with escaped ()' \
|
||||
'http://a.com/\(foo\)' \
|
||||
'<p><a href="http://a.com/(foo)">http://a.com/(foo)</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with escaped \' \
|
||||
'http://a.com/\\\)' \
|
||||
'<p><a href="http://a.com/\)">http://a.com/\)</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with -' \
|
||||
'http://experts-exchange.com' \
|
||||
'<p><a href="http://experts-exchange.com">http://experts-exchange.com</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with +' \
|
||||
'http://www67.wolframalpha.com/input/?i=how+old+was+jfk+jr+when+jfk+died' \
|
||||
'<p><a href="http://www67.wolframalpha.com/input/?i=how+old+was+jfk+jr+when+jfk+died">http://www67.wolframalpha.com/input/?i=how+old+was+jfk+jr+when+jfk+died</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with &' \
|
||||
'http://foo.bar?a&b=c' \
|
||||
'<p><a href="http://foo.bar?a&b=c">http://foo.bar?a&b=c</a></p>'
|
||||
|
||||
|
||||
try -fautolink 'autolink url with ,' \
|
||||
'http://www.spiegel.de/international/europe/0,1518,626171,00.html' \
|
||||
'<p><a href="http://www.spiegel.de/international/europe/0,1518,626171,00.html">http://www.spiegel.de/international/europe/0,1518,626171,00.html</a></p>'
|
||||
|
||||
try -fautolink 'autolink url with : & ;' \
|
||||
'http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&version=31;' \
|
||||
'<p><a href="http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&version=31;">http://www.biblegateway.com/passage/?search=Matthew%205:29-30;&version=31;</a></p>'
|
||||
|
||||
Q="'"
|
||||
try -fautolink 'security hole with \" in []()' \
|
||||
'[XSS](/ "\"=\"\"onmouseover='$Q'alert(String.fromCharCode(88,83,83))'$Q'")' \
|
||||
'<p><a href="/" title="\"=\"\"onmouseover='$Q'alert(String.fromCharCode(88,83,83))'$Q'">XSS</a></p>'
|
||||
|
||||
|
||||
exit $rc
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user