--- /dev/null
+/* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * Loading URLs and returning the underlying text.
+ *
+ * This is necessary because iOS doesn't have Perl installed, so we can't
+ * run "xscreensaver-text" to do this.
+ */
+
+#include "utils.h"
+
+#ifdef USE_IPHONE /* whole file -- see utils/textclient.c */
+
+#include "textclient.h"
+#include "resources.h"
+
+#include <stdio.h>
+
+extern const char *progname;
+
+struct text_data {
+
+ enum { DATE, LITERAL, URL } mode;
+ char *literal, *url;
+
+ int columns;
+ char *buf;
+ int buf_size;
+ char *fp;
+
+};
+
+
+void
+textclient_reshape (text_data *d,
+ int pix_w, int pix_h,
+ int char_w, int char_h)
+{
+ d->columns = char_w;
+}
+
+
+text_data *
+textclient_open (Display *dpy)
+{
+ text_data *d = (text_data *) calloc (1, sizeof (*d));
+
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: init\n", progname);
+# endif
+
+ char *s = get_string_resource (dpy, "textMode", "TextMode");
+ if (!s || !*s || !strcasecmp (s, "date") || !strcmp (s, "0"))
+ d->mode = DATE;
+ else if (!strcasecmp (s, "literal") || !strcmp (s, "1"))
+ d->mode = LITERAL;
+ else if (!strcasecmp (s, "url") || !strcmp (s, "3"))
+ d->mode = URL;
+ else
+ d->mode = DATE;
+
+ d->literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
+ d->url = get_string_resource (dpy, "textURL", "TextURL");
+
+ return d;
+}
+
+
+void
+textclient_close (text_data *d)
+{
+# ifdef DEBUG
+ fprintf (stderr, "%s: textclient: free\n", progname);
+# endif
+
+ if (d->buf) free (d->buf);
+ if (d->literal) free (d->literal);
+ if (d->url) free (d->url);
+ free (d);
+}
+
+
+static char *
+date_string (void)
+{
+ UIDevice *dd = [UIDevice currentDevice];
+ NSString *name = [dd name]; // My iPhone
+ NSString *model = [dd model]; // iPad
+ // NSString *system = [dd systemName]; // iPhone OS
+ NSString *vers = [dd systemVersion]; // 5.0
+ NSString *date =
+ [NSDateFormatter
+ localizedStringFromDate:[NSDate date]
+ dateStyle: NSDateFormatterMediumStyle
+ timeStyle: NSDateFormatterMediumStyle];
+ NSString *nl = @"\n";
+
+ NSString *result = name;
+ result = [result stringByAppendingString: nl];
+ result = [result stringByAppendingString: model];
+ // result = [result stringByAppendingString: nl];
+ // result = [result stringByAppendingString: system];
+ result = [result stringByAppendingString: @" "];
+ result = [result stringByAppendingString: vers];
+ result = [result stringByAppendingString: nl];
+ result = [result stringByAppendingString: nl];
+ result = [result stringByAppendingString: date];
+ result = [result stringByAppendingString: nl];
+ result = [result stringByAppendingString: nl];
+ return strdup ([result cStringUsingEncoding:NSISOLatin1StringEncoding]);
+}
+
+
+static void
+strip_html (char *html)
+{
+ int tag = 0;
+ int comment = 0;
+ int white = 0;
+ int nl = 0;
+ int entity = 0;
+ char *out = html;
+ for (char *in = html; *in; in++) {
+ if (comment) {
+ if (!strncmp (in, "-->", 3)) {
+ comment = 0;
+ }
+ } else if (tag) {
+ if (*in == '>') {
+ tag = 0;
+ entity = 0;
+ }
+ } else if (entity) {
+ if (*in == ';') {
+ entity = 0;
+ }
+ } else if (*in == '<') {
+ tag = 1;
+ entity = 0;
+ if (!strncmp (in, "<!--", 4)) {
+ comment = 1;
+ tag = 0;
+ } else if (!strncasecmp (in, "<BR", 3)) {
+ *out++ = '\n';
+ white = 1;
+ nl++;
+ } else if (!strncasecmp (in, "<P", 2)) {
+ if (nl < 2) { *out++ = '\n'; nl++; }
+ if (nl < 2) { *out++ = '\n'; nl++; }
+ white = 1;
+ }
+ } else if (*in == '&') {
+ char *ss = 0;
+ entity = 1;
+
+ if (!strncmp (in, "&", 4)) ss = "&";
+ else if (!strncmp (in, "<", 3)) ss = "<";
+ else if (!strncmp (in, ">", 3)) ss = ">";
+ else if (!strncmp (in, " ", 5)) ss = " ";
+
+ else if (!strncmp (in, "Æ", 6)) ss = "AE";
+ else if (!strncmp (in, "æ", 6)) ss = "ae";
+ else if (!strncmp (in, "&bdquo", 6)) ss = "\"";
+ else if (!strncmp (in, "¦", 7)) ss = "|";
+ else if (!strncmp (in, "&bull", 5)) ss = "*";
+ else if (!strncmp (in, "&circ", 5)) ss = "^";
+ else if (!strncmp (in, "&cong", 5)) ss = "=~";
+ else if (!strncmp (in, "©", 5)) ss = "(c)";
+ else if (!strncmp (in, "¤", 7)) ss = "$";
+ else if (!strncmp (in, "°", 4)) ss = ".";
+ else if (!strncmp (in, "÷", 7)) ss = "/";
+ else if (!strncmp (in, "&empty", 6)) ss = "0";
+ else if (!strncmp (in, "&emsp", 5)) ss = " ";
+ else if (!strncmp (in, "&ensp", 5)) ss = " ";
+ else if (!strncmp (in, "&equiv", 6)) ss = "==";
+ else if (!strncmp (in, "½", 7)) ss = "1/2";
+ else if (!strncmp (in, "¼", 7)) ss = "1/4";
+ else if (!strncmp (in, "¾", 7)) ss = "3/4";
+ else if (!strncmp (in, "&frasl", 6)) ss = "/";
+ else if (!strncmp (in, "&ge", 3)) ss = ">=";
+ else if (!strncmp (in, "&hArr", 5)) ss = "<=>";
+ else if (!strncmp (in, "&harr", 5)) ss = "<->";
+ else if (!strncmp (in, "&hellip", 7)) ss = "...";
+ else if (!strncmp (in, "¿", 7)) ss = "?";
+ else if (!strncmp (in, "&lArr", 5)) ss = "<=";
+ else if (!strncmp (in, "&lang", 5)) ss = "<";
+ else if (!strncmp (in, "«", 6)) ss = "<<";
+ else if (!strncmp (in, "&larr", 5)) ss = "<-";
+ else if (!strncmp (in, "&ldquo", 6)) ss = "\"";
+ else if (!strncmp (in, "&le", 3)) ss = "<=";
+ else if (!strncmp (in, "&lowast", 7)) ss = "*";
+ else if (!strncmp (in, "&loz", 4)) ss = "<>";
+ else if (!strncmp (in, "&lsaquo", 7)) ss = "<";
+ else if (!strncmp (in, "&lsquo", 6)) ss = "`";
+ else if (!strncmp (in, "¯", 5)) ss = "'";
+ else if (!strncmp (in, "&mdash", 6)) ss = "--";
+ else if (!strncmp (in, "µ", 6)) ss = "u";
+ else if (!strncmp (in, "·", 7)) ss = ".";
+ else if (!strncmp (in, "&minus", 6)) ss = "-";
+ else if (!strncmp (in, "&ndash", 6)) ss = "-";
+ else if (!strncmp (in, "&ne", 3)) ss = "!=";
+ else if (!strncmp (in, "¬", 4)) ss = "!";
+ else if (!strncmp (in, "&OElig", 6)) ss = "OE";
+ else if (!strncmp (in, "&oelig", 6)) ss = "oe";
+ else if (!strncmp (in, "ª", 5)) ss = "_";
+ else if (!strncmp (in, "º", 5)) ss = "_";
+ else if (!strncmp (in, "¶", 5)) ss = "PP";
+ else if (!strncmp (in, "±", 7)) ss = "+/-";
+ else if (!strncmp (in, "£", 6)) ss = "#";
+ else if (!strncmp (in, "&prime", 6)) ss = "'";
+ else if (!strncmp (in, """, 5)) ss = "\"";
+ else if (!strncmp (in, "&rArr", 5)) ss = "=>";
+ else if (!strncmp (in, "&rang", 5)) ss = ">";
+ else if (!strncmp (in, "»", 6)) ss = ">>";
+ else if (!strncmp (in, "&rarr", 5)) ss = "->";
+ else if (!strncmp (in, "&rdquo", 6)) ss = "\"";
+ else if (!strncmp (in, "®", 4)) ss = "(r)";
+ else if (!strncmp (in, "&rsaquo", 7)) ss = ">";
+ else if (!strncmp (in, "&rsquo", 6)) ss = "'";
+ else if (!strncmp (in, "&sbquo", 6)) ss = "'";
+ else if (!strncmp (in, "§", 5)) ss = "SS";
+ else if (!strncmp (in, "­", 4)) ss = "";
+ else if (!strncmp (in, "&sim", 4)) ss = "~";
+ else if (!strncmp (in, "¹", 5)) ss = "[1]";
+ else if (!strncmp (in, "²", 5)) ss = "[2]";
+ else if (!strncmp (in, "³", 5)) ss = "[3]";
+ else if (!strncmp (in, "ß", 6)) ss = "B";
+ else if (!strncmp (in, "&thinsp", 7)) ss = " ";
+ else if (!strncmp (in, "þ", 6)) ss = "|";
+ else if (!strncmp (in, "&tilde", 6)) ss = "!";
+ else if (!strncmp (in, "×", 6)) ss = "x";
+ else if (!strncmp (in, "&trade", 6)) ss = "[tm]";
+ else if (!strncmp (in, "¨", 4)) ss = ":";
+ else if (!strncmp (in, "¥", 4)) ss = "Y";
+
+ if (ss) {
+ strcpy (out, ss);
+ out += strlen(ss);
+ } else if (!strncmp (in, "&#", 2)) { // A
+ int i = 0;
+ for (char *in2 = in+2; *in2 >= '0' && *in2 <= '9'; in2++)
+ i = (i * 10) + (*in2 - '0');
+ *out = (i > 255 ? '?' : i);
+ } else if (!strncmp (in, "&#x", 3)) { // A
+ int i = 0;
+ for (char *in2 = in+3;
+ ((*in2 >= '0' && *in2 <= '9') ||
+ (*in2 >= 'A' && *in2 <= 'F') ||
+ (*in2 >= 'a' && *in2 <= 'f'));
+ in2++)
+ i = (i * 16) + (*in2 >= 'a' ? *in2 - 'a' + 16 :
+ *in2 >= 'A' ? *in2 - 'A' + 16 :
+ *in2 - '0');
+ *out = (i > 255 ? '?' : i);
+ } else {
+ *out++ = in[1]; // first character of entity, e.g. é.
+ }
+ } else if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
+ if (!white && out != html)
+ *out++ = ' ';
+ white = 1;
+ } else {
+ *out++ = *in;
+ white = 0;
+ nl = 0;
+ }
+ }
+ *out = 0;
+}
+
+
+static char *
+copy_rss_field (const char *s)
+{
+ if (!s) return 0;
+ while (*s && *s != '>') // Skip forward to >
+ s++;
+ if (! *s) return 0;
+ s++;
+
+ if (!strncmp (s, "<![CDATA[", 9)) { // CDATA quoting
+ s += 9;
+ char *e = strstr (s, "]]");
+ if (e) *e = 0;
+ int L = strlen (s);
+ char *s2 = (char *) malloc (L+1);
+ memcpy (s2, s, L+1);
+ return s2;
+
+ } else { // Entity-encoded.
+ const char *s2;
+ for (s2 = s; *s2 && *s2 != '<'; s2++) // Terminate at <
+ ;
+ char *s3 = (char *) malloc (s2 - s + 1);
+ if (! s3) return 0;
+ memcpy (s3, s, s2-s);
+ s3[s2-s] = 0;
+ strip_html (s3);
+ return s3;
+ }
+}
+
+
+static char *
+pick_rss_field (const char *a, const char *b, const char *c, const char *d)
+{
+ // Pick the longest of the fields.
+ char *a2 = copy_rss_field (a);
+ char *b2 = copy_rss_field (b);
+ char *c2 = copy_rss_field (c);
+ char *d2 = copy_rss_field (d);
+ int al = a2 ? strlen(a2) : 0;
+ int bl = b2 ? strlen(b2) : 0;
+ int cl = c2 ? strlen(c2) : 0;
+ int dl = d2 ? strlen(d2) : 0;
+ char *ret = 0;
+
+ if (al > bl && al > cl && al > dl) ret = a2;
+ else if (bl > al && bl > cl && bl > dl) ret = b2;
+ else if (cl > al && cl > bl && cl > dl) ret = c2;
+ else ret = d2;
+ if (a2 && a2 != ret) free (a2);
+ if (b2 && b2 != ret) free (b2);
+ if (c2 && c2 != ret) free (c2);
+ if (d2 && d2 != ret) free (d2);
+ return ret;
+}
+
+
+static void
+strip_rss (char *rss)
+{
+ char *out = rss;
+ const char *a = 0, *b = 0, *c = 0, *d = 0, *t = 0;
+ int head = 1;
+ int done = 0;
+
+ for (char *in = rss; *in; in++) {
+ if (*in == '<') {
+ if (!strncasecmp (in, "<item", 5) || // New item, dump.
+ !strncasecmp (in, "<entry", 6)) {
+ DONE:
+ head = 0;
+ char *title = copy_rss_field (t);
+ char *body = pick_rss_field (a, b, c, d);
+
+ a = b = c = d = t = 0;
+
+ if (title && body && !strcmp (title, body)) {
+ free (title);
+ title = 0;
+ }
+
+ if (title) {
+ strcpy (out, title);
+ free (title);
+ out += strlen (out);
+ strcpy (out, "\n\n");
+ out += strlen (out);
+ }
+
+ if (body) {
+ strcpy (out, body);
+ free (body);
+ out += strlen (out);
+ strcpy (out, "<P>");
+ out += strlen (out);
+ }
+
+ } else if (head) { // still before first <item>
+ ;
+ } else if (!strncasecmp (in, "<title", 6)) {
+ t = in+6;
+ } else if (!strncasecmp (in, "<summary", 8)) {
+ d = in+8;
+ } else if (!strncasecmp (in, "<description", 12)) {
+ a = in+12;
+ } else if (!strncasecmp (in, "<content:encoded", 16)) {
+ c = in+16;
+ } else if (!strncasecmp (in, "<content", 8)) {
+ b = in+8;
+ }
+ }
+ }
+
+ if (! done) { // Finish off the final item.
+ done = 1;
+ goto DONE;
+ }
+
+ // Now decode it a second time.
+ strip_html (rss);
+}
+
+
+static void
+wrap_text (char *body, int columns)
+{
+ int col = 0, last_col = 0;
+ char *last_space = 0;
+ for (char *p = body; *p; p++) {
+ if (*p == '\r' || *p == '\n' || *p == ' ' || *p == '\t') {
+ if (col > columns && last_space) {
+ *last_space = '\n';
+ col = col - last_col;
+ }
+ last_space = p;
+ last_col = col;
+ }
+ if (*p == '\r' || *p == '\n') {
+ col = 0;
+ last_col = 0;
+ last_space = 0;
+ } else {
+ col++;
+ }
+ }
+}
+
+
+static void
+strip_backslashes (char *s)
+{
+ char *out = s;
+ for (char *in = s; *in; in++) {
+ if (*in == '\\') {
+ in++;
+ if (*in == 'n') *out++ = '\n';
+ else if (*in == 'r') *out++ = '\r';
+ else if (*in == 't') *out++ = '\t';
+ else *out++ = *in;
+ } else {
+ *out++ = *in;
+ }
+ }
+ *out = 0;
+}
+
+
+static char *
+url_string (const char *url)
+{
+ NSURL *nsurl =
+ [NSURL URLWithString:
+ [NSString stringWithCString: url
+ encoding:NSISOLatin1StringEncoding]];
+ NSString *body =
+ [NSString stringWithContentsOfURL: nsurl
+ encoding: NSISOLatin1StringEncoding
+ error: nil];
+ if (! body)
+ return 0;
+
+ enum { RSS, HTML, TEXT } type;
+
+ // Only search the first 1/2 K of the document while determining type.
+
+ int L = [body length];
+ if (L > 512) L = 512;
+ NSString *head = [[[body substringToIndex: L]
+ stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]]
+ lowercaseString];
+ if ([head hasPrefix:@"<?xml"] ||
+ [head hasPrefix:@"<!doctype rss"])
+ type = RSS;
+ else if ([head hasPrefix:@"<!doctype html"] ||
+ [head hasPrefix:@"<html"] ||
+ [head hasPrefix:@"<head"])
+ type = HTML;
+ else if ([head rangeOfString:@"<base"].length ||
+ [head rangeOfString:@"<body"].length ||
+ [head rangeOfString:@"<script"].length ||
+ [head rangeOfString:@"<style"].length ||
+ [head rangeOfString:@"<a href"].length)
+ type = HTML;
+ else if ([head rangeOfString:@"<channel"].length ||
+ [head rangeOfString:@"<generator"].length ||
+ [head rangeOfString:@"<description"].length ||
+ [head rangeOfString:@"<content"].length ||
+ [head rangeOfString:@"<feed"].length ||
+ [head rangeOfString:@"<entry"].length)
+ type = RSS;
+ else
+ type = TEXT;
+
+ char *body2 = strdup ([body cStringUsingEncoding:NSISOLatin1StringEncoding]);
+
+ switch (type) {
+ case HTML: strip_html (body2); break;
+ case RSS: strip_rss (body2); break;
+ case TEXT: break;
+ default: abort(); break;
+ }
+
+ return body2;
+}
+
+
+int
+textclient_getc (text_data *d)
+{
+ if (!d->fp || !*d->fp) {
+ if (d->buf) {
+ free (d->buf);
+ d->buf = 0;
+ d->fp = 0;
+ }
+ switch (d->mode) {
+ case DATE: DATE:
+ d->buf = date_string();
+ break;
+ case LITERAL:
+ if (!d->literal || !*d->literal)
+ goto DATE;
+ d->buf = (char *) malloc (strlen (d->literal) + 3);
+ strcpy (d->buf, d->literal);
+ strcat (d->buf, "\n");
+ strip_backslashes (d->buf);
+ d->fp = d->buf;
+ break;
+ case URL:
+ if (!d->url || !*d->url)
+ goto DATE;
+ d->buf = url_string (d->url);
+ break;
+ default:
+ abort();
+ }
+ if (d->columns > 10)
+ wrap_text (d->buf, d->columns);
+ d->fp = d->buf;
+ }
+
+ if (!d->fp || !*d->fp)
+ return -1;
+
+ unsigned char c = (unsigned char) *d->fp++;
+ return (int) c;
+}
+
+
+Bool
+textclient_putc (text_data *d, XKeyEvent *k)
+{
+ return False;
+}
+
+#endif /* USE_IPHONE -- whole file */