rssread revision da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
#
# rssread - a simple RSS2.0 reader with RSS to XHTML to
# plaintext conversion.
#
function printmsg
{
print -u 2 "$@"
}
function debugmsg
{
# printmsg "$@"
true
}
function fatal_error
{
print -u 2 "${progname}: $@"
exit 1
}
function cat_http
{
(
protocol="${1%://*}"
host="${path1%%/*}"
path="${path1#*/}"
port="${host##*:}"
# If URL did not contain a port number in the host part then look at the
# protocol to get the port number
if [ "${port}" = "${host}" ] ; then
esac
else
host="${host%:*}"
fi
printmsg "protocol=${protocol} port=${port} host=${host} path=${path}"
# prechecks
# open TCP channel
# send HTTP request
request="GET /${path} HTTP/1.0\n"
request+="Host: ${host}\n"
print "${request}\n" >&3
# collect response and send it to stdout
cat <&3
)
}
function html_entity_to_ascii
{
typeset -A entity_cache
buf=""
while read -r -N 1 c ; do
if [ "$c" != "&" ] ; then
printf "%s" "${c}"
continue
fi
entity=""
while read -r -N 1 c ; do
";")
break
;;
entity+="$c"
continue
;;
*)
debugmsg "error &${entity}${c}#"
print -n "${entity}${c}"
entity=""
continue 2
;;
esac
done
value=""
if [ "${entity_cache["${entity}"]}" != "" ] ; then
else
if [ "${entity:0:1}" = "#" ] ; then
# decimal literal
# hexadecimal literal
else
# unknown literal - pass-through
value="<ENT=${entity}>"
fi
fi
done
}
# dumb xhtml handler - no CSS, tables, images, iframes or nested
# structures are supported (and we assume that the input is correct
# xhtml). The code was written in a trial&&error manner and should be
# rewritten to parse xhtml correctly.
function handle_html
{
# we can't use global variables here when multiple callbacks use the same
# callback function - but we can use the callback associative array for
# variable storage instead
nameref callbacks=${1}
tag_type="$2"
tag_value="$3"
esac
;;
esac
;;
printf "%s" "${tag_value}"
else
printf "%s" "${tag_value/+([\n\r\t\v[:space:][:blank:]])/ }"
fi
;;
;;
document_end) ;;
esac
}
function handle_rss
{
# we can't use global variables here when multiple callbacks use the same
# callback function - but we can use the callback associative array for
# variable storage instead
nameref callbacks=${1}
tag_type="$2"
tag_value="$3"
item*)
;;
esac
;;
item*)
# note that each RSS item needs to be converted seperately from RSS to HTML to plain text
# to make sure that the state of one RSS item doesn't affect others
(
printf $"<br />## begin description:"
printf $"<br />## end description<br />"
print # extra newline to make sure the sed pipeline gets flushed
) |
html_entity_to_ascii | # convert XML entities (e.g. decode RSS content to HTML code)
html_entity_to_ascii # convert HTML entities
;;
esac
;;
;;
document_start) ;;
document_end) ;;
esac
}
function xml_tok
{
typeset buf=""
typeset c=""
nameref callbacks=${1}
while read -N 1 c -d '\0'; do
isendtag=false
if [ "$c" = "<" ] ; then
buf=""
fi
read -N 1 c -d '\0'
if [ "$c" = "/" ] ; then
isendtag=true
else
buf="$c"
fi
read -d '>' c
buf+="$c"
if ${isendtag} ; then
else
# handle tags like <br/> (which are start- and end-tag in one piece)
fi
fi
buf=""
else
buf+="$c"
fi
done
[ ! -z "${callbacks["document_end"]}" ] && ${callbacks["document_start"]} "${1}" "document_end" "exit_success"
print # final newline to make filters like "sed" happy
}
# return the value of LC_MESSAGES needed for subprocesses which
function get_lc_messages
{
[ "${LC_ALL}" != "" ] && { print "${LC_ALL}" ; return 0 ; }
[ "${LC_MESSAGES}" != "" ] && { print "${LC_MESSAGES}" ; return 0 ; }
[ "${LANG}" != "" ] && { print "${LANG}" ; return 0 ; }
print "C" ; return 0
}
function usage
{
OPTIND=0
exit 2
}
# make sure we use the ksh93 builtin versions
builtin cat
builtin printf
typeset -A rsstok_cb # callbacks for xml_tok
typeset -A xhtmltok_cb # callbacks for xml_tok
typeset -A item
typeset -A bookmark_urls
# "ramdom" urls for testing
["google_blogs_ksh"]="http://blogsearch.google.com/blogsearch_feeds?hl=en&scoring=d&q=ksh&ie=utf-8&num=100&output=rss"
["ksh93_integration"]="http://www.opensolaris.org/rss/os/project/ksh93-integration/announcements/rss2.xml"
)
progname="${0}"
USAGE=$'
[-?
@(#)\$Id: rssread (Roland Mainz) 2007-06-05 \$
]
[+NAME?rssread - fetch RSS messages and convert them to plain text]
[+DESCRIPTION?\brssread\b RSS to plain text converter
which fetches RSS streams via HTTP and converts them from RSS to HTML to plain UTF-8 text.]
[ url ]
[+SEE ALSO?\bksh93\b(1)]
'
while getopts -a "${progname}" "${USAGE}" OPT ; do
# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
*) usage ;;
esac
done
shift ${OPTIND}-1
url="$1"
if [ "$url" = "" ] ; then
fatal_error $"No url given."
fi
if [ "${bookmark_urls[${url}]}" != "" ] ; then
printmsg $"Using bookmark ${url} = ${bookmark_urls[${url}]}"
url="${bookmark_urls[${url}]}"
fi
(
# set unicode locale since RSS is encoded in UTF-8
# (and make sure $LC_MESSAGES is set to the parent
# process's locale that all error messages are using
export \
xml_tok "rsstok_cb"
) # | iconv -f "UTF-8" - -
#EOF.