1 /* xscreensaver, Copyright (c) 2012 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Loading URLs and returning the underlying text.
13 * This is necessary because iOS doesn't have Perl installed, so we can't
14 * run "xscreensaver-text" to do this.
19 #ifdef USE_IPHONE /* whole file -- see utils/textclient.c */
21 #include "textclient.h"
22 #include "resources.h"
26 extern const char *progname;
30 enum { DATE, LITERAL, URL } mode;
42 textclient_open (Display *dpy)
44 text_data *d = (text_data *) calloc (1, sizeof (*d));
47 fprintf (stderr, "%s: textclient: init\n", progname);
50 char *s = get_string_resource (dpy, "textMode", "TextMode");
51 if (!s || !*s || !strcasecmp (s, "date") || !strcmp (s, "0"))
53 else if (!strcasecmp (s, "literal") || !strcmp (s, "1"))
55 else if (!strcasecmp (s, "url") || !strcmp (s, "3"))
60 d->literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
61 d->url = get_string_resource (dpy, "textURL", "TextURL");
68 textclient_close (text_data *d)
71 fprintf (stderr, "%s: textclient: free\n", progname);
74 if (d->buf) free (d->buf);
75 if (d->literal) free (d->literal);
76 if (d->url) free (d->url);
84 UIDevice *dd = [UIDevice currentDevice];
85 NSString *name = [dd name]; // My iPhone
86 NSString *model = [dd model]; // iPad
87 // NSString *system = [dd systemName]; // iPhone OS
88 NSString *vers = [dd systemVersion]; // 5.0
91 localizedStringFromDate:[NSDate date]
92 dateStyle: NSDateFormatterMediumStyle
93 timeStyle: NSDateFormatterMediumStyle];
96 NSString *result = name;
97 result = [result stringByAppendingString: nl];
98 result = [result stringByAppendingString: model];
99 // result = [result stringByAppendingString: nl];
100 // result = [result stringByAppendingString: system];
101 result = [result stringByAppendingString: @" "];
102 result = [result stringByAppendingString: vers];
103 result = [result stringByAppendingString: nl];
104 result = [result stringByAppendingString: nl];
105 result = [result stringByAppendingString: date];
106 result = [result stringByAppendingString: nl];
107 result = [result stringByAppendingString: nl];
108 return strdup ([result cStringUsingEncoding:NSISOLatin1StringEncoding]);
113 strip_html (char *html)
121 for (char *in = html; *in; in++) {
123 if (!strncmp (in, "-->", 3)) {
135 } else if (*in == '<') {
138 if (!strncmp (in, "<!--", 4)) {
141 } else if (!strncasecmp (in, "<BR", 3)) {
145 } else if (!strncasecmp (in, "<P", 2)) {
146 if (nl < 2) { *out++ = '\n'; nl++; }
147 if (nl < 2) { *out++ = '\n'; nl++; }
150 } else if (*in == '&') {
154 if (!strncmp (in, "&", 4)) ss = "&";
155 else if (!strncmp (in, "<", 3)) ss = "<";
156 else if (!strncmp (in, ">", 3)) ss = ">";
157 else if (!strncmp (in, " ", 5)) ss = " ";
159 else if (!strncmp (in, "Æ", 6)) ss = "AE";
160 else if (!strncmp (in, "æ", 6)) ss = "ae";
161 else if (!strncmp (in, "&bdquo", 6)) ss = "\"";
162 else if (!strncmp (in, "¦", 7)) ss = "|";
163 else if (!strncmp (in, "&bull", 5)) ss = "*";
164 else if (!strncmp (in, "&circ", 5)) ss = "^";
165 else if (!strncmp (in, "&cong", 5)) ss = "=~";
166 else if (!strncmp (in, "©", 5)) ss = "(c)";
167 else if (!strncmp (in, "¤", 7)) ss = "$";
168 else if (!strncmp (in, "°", 4)) ss = ".";
169 else if (!strncmp (in, "÷", 7)) ss = "/";
170 else if (!strncmp (in, "&empty", 6)) ss = "0";
171 else if (!strncmp (in, "&emsp", 5)) ss = " ";
172 else if (!strncmp (in, "&ensp", 5)) ss = " ";
173 else if (!strncmp (in, "&equiv", 6)) ss = "==";
174 else if (!strncmp (in, "½", 7)) ss = "1/2";
175 else if (!strncmp (in, "¼", 7)) ss = "1/4";
176 else if (!strncmp (in, "¾", 7)) ss = "3/4";
177 else if (!strncmp (in, "&frasl", 6)) ss = "/";
178 else if (!strncmp (in, "&ge", 3)) ss = ">=";
179 else if (!strncmp (in, "&hArr", 5)) ss = "<=>";
180 else if (!strncmp (in, "&harr", 5)) ss = "<->";
181 else if (!strncmp (in, "&hellip", 7)) ss = "...";
182 else if (!strncmp (in, "¿", 7)) ss = "?";
183 else if (!strncmp (in, "&lArr", 5)) ss = "<=";
184 else if (!strncmp (in, "&lang", 5)) ss = "<";
185 else if (!strncmp (in, "«", 6)) ss = "<<";
186 else if (!strncmp (in, "&larr", 5)) ss = "<-";
187 else if (!strncmp (in, "&ldquo", 6)) ss = "\"";
188 else if (!strncmp (in, "&le", 3)) ss = "<=";
189 else if (!strncmp (in, "&lowast", 7)) ss = "*";
190 else if (!strncmp (in, "&loz", 4)) ss = "<>";
191 else if (!strncmp (in, "&lsaquo", 7)) ss = "<";
192 else if (!strncmp (in, "&lsquo", 6)) ss = "`";
193 else if (!strncmp (in, "¯", 5)) ss = "'";
194 else if (!strncmp (in, "&mdash", 6)) ss = "--";
195 else if (!strncmp (in, "µ", 6)) ss = "u";
196 else if (!strncmp (in, "·", 7)) ss = ".";
197 else if (!strncmp (in, "&minus", 6)) ss = "-";
198 else if (!strncmp (in, "&ndash", 6)) ss = "-";
199 else if (!strncmp (in, "&ne", 3)) ss = "!=";
200 else if (!strncmp (in, "¬", 4)) ss = "!";
201 else if (!strncmp (in, "&OElig", 6)) ss = "OE";
202 else if (!strncmp (in, "&oelig", 6)) ss = "oe";
203 else if (!strncmp (in, "ª", 5)) ss = "_";
204 else if (!strncmp (in, "º", 5)) ss = "_";
205 else if (!strncmp (in, "¶", 5)) ss = "PP";
206 else if (!strncmp (in, "±", 7)) ss = "+/-";
207 else if (!strncmp (in, "£", 6)) ss = "#";
208 else if (!strncmp (in, "&prime", 6)) ss = "'";
209 else if (!strncmp (in, """, 5)) ss = "\"";
210 else if (!strncmp (in, "&rArr", 5)) ss = "=>";
211 else if (!strncmp (in, "&rang", 5)) ss = ">";
212 else if (!strncmp (in, "»", 6)) ss = ">>";
213 else if (!strncmp (in, "&rarr", 5)) ss = "->";
214 else if (!strncmp (in, "&rdquo", 6)) ss = "\"";
215 else if (!strncmp (in, "®", 4)) ss = "(r)";
216 else if (!strncmp (in, "&rsaquo", 7)) ss = ">";
217 else if (!strncmp (in, "&rsquo", 6)) ss = "'";
218 else if (!strncmp (in, "&sbquo", 6)) ss = "'";
219 else if (!strncmp (in, "§", 5)) ss = "SS";
220 else if (!strncmp (in, "­", 4)) ss = "";
221 else if (!strncmp (in, "&sim", 4)) ss = "~";
222 else if (!strncmp (in, "¹", 5)) ss = "[1]";
223 else if (!strncmp (in, "²", 5)) ss = "[2]";
224 else if (!strncmp (in, "³", 5)) ss = "[3]";
225 else if (!strncmp (in, "ß", 6)) ss = "B";
226 else if (!strncmp (in, "&thinsp", 7)) ss = " ";
227 else if (!strncmp (in, "þ", 6)) ss = "|";
228 else if (!strncmp (in, "&tilde", 6)) ss = "!";
229 else if (!strncmp (in, "×", 6)) ss = "x";
230 else if (!strncmp (in, "&trade", 6)) ss = "[tm]";
231 else if (!strncmp (in, "¨", 4)) ss = ":";
232 else if (!strncmp (in, "¥", 4)) ss = "Y";
237 } else if (!strncmp (in, "&#", 2)) { // A
239 for (char *in2 = in+2; *in2 >= '0' && *in2 <= '9'; in2++)
240 i = (i * 10) + (*in2 - '0');
241 *out = (i > 255 ? '?' : i);
242 } else if (!strncmp (in, "&#x", 3)) { // A
244 for (char *in2 = in+3;
245 ((*in2 >= '0' && *in2 <= '9') ||
246 (*in2 >= 'A' && *in2 <= 'F') ||
247 (*in2 >= 'a' && *in2 <= 'f'));
249 i = (i * 16) + (*in2 >= 'a' ? *in2 - 'a' + 16 :
250 *in2 >= 'A' ? *in2 - 'A' + 16 :
252 *out = (i > 255 ? '?' : i);
254 *out++ = in[1]; // first character of entity, e.g. é.
256 } else if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
257 if (!white && out != html)
271 copy_rss_field (const char *s)
274 while (*s && *s != '>') // Skip forward to >
279 if (!strncmp (s, "<![CDATA[", 9)) { // CDATA quoting
281 char *e = strstr (s, "]]");
283 unsigned long L = strlen (s);
284 char *s2 = (char *) malloc (L+1);
288 } else { // Entity-encoded.
290 for (s2 = s; *s2 && *s2 != '<'; s2++) // Terminate at <
292 char *s3 = (char *) malloc (s2 - s + 1);
294 memcpy (s3, s, s2-s);
303 pick_rss_field (const char *a, const char *b, const char *c, const char *d)
305 // Pick the longest of the fields.
306 char *a2 = copy_rss_field (a);
307 char *b2 = copy_rss_field (b);
308 char *c2 = copy_rss_field (c);
309 char *d2 = copy_rss_field (d);
310 unsigned long al = a2 ? strlen(a2) : 0;
311 unsigned long bl = b2 ? strlen(b2) : 0;
312 unsigned long cl = c2 ? strlen(c2) : 0;
313 unsigned long dl = d2 ? strlen(d2) : 0;
316 if (al > bl && al > cl && al > dl) ret = a2;
317 else if (bl > al && bl > cl && bl > dl) ret = b2;
318 else if (cl > al && cl > bl && cl > dl) ret = c2;
320 if (a2 && a2 != ret) free (a2);
321 if (b2 && b2 != ret) free (b2);
322 if (c2 && c2 != ret) free (c2);
323 if (d2 && d2 != ret) free (d2);
329 strip_rss (char *rss)
332 const char *a = 0, *b = 0, *c = 0, *d = 0, *t = 0;
336 for (char *in = rss; *in; in++) {
338 if (!strncasecmp (in, "<item", 5) || // New item, dump.
339 !strncasecmp (in, "<entry", 6)) {
342 char *title = copy_rss_field (t);
343 char *body = pick_rss_field (a, b, c, d);
345 a = b = c = d = t = 0;
347 if (title && body && !strcmp (title, body)) {
356 strcpy (out, "\n\n");
368 } else if (head) { // still before first <item>
370 } else if (!strncasecmp (in, "<title", 6)) {
372 } else if (!strncasecmp (in, "<summary", 8)) {
374 } else if (!strncasecmp (in, "<description", 12)) {
376 } else if (!strncasecmp (in, "<content:encoded", 16)) {
378 } else if (!strncasecmp (in, "<content", 8)) {
384 if (! done) { // Finish off the final item.
389 // Now decode it a second time.
395 wrap_text (char *body, int columns)
397 int col = 0, last_col = 0;
398 char *last_space = 0;
400 for (char *p = body; *p; p++) {
401 if (*p == '\r' || *p == '\n' || *p == ' ' || *p == '\t') {
402 if (col > columns && last_space) {
404 col = col - last_col;
409 if (*p == '\r' || *p == '\n') {
421 rewrap_text (char *body, int columns)
424 for (char *p = body; *p; p++) {
432 wrap_text (body, columns);
438 strip_backslashes (char *s)
441 for (char *in = s; *in; in++) {
444 if (*in == 'n') *out++ = '\n';
445 else if (*in == 'r') *out++ = '\r';
446 else if (*in == 't') *out++ = '\t';
457 url_string (const char *url)
460 [NSURL URLWithString:
461 [NSString stringWithCString: url
462 encoding:NSISOLatin1StringEncoding]];
464 [NSString stringWithContentsOfURL: nsurl
465 encoding: NSISOLatin1StringEncoding
470 enum { RSS, HTML, TEXT } type;
472 // Only search the first 1/2 K of the document while determining type.
474 unsigned long L = [body length];
475 if (L > 512) L = 512;
476 NSString *head = [[[body substringToIndex: L]
477 stringByTrimmingCharactersInSet:
478 [NSCharacterSet whitespaceAndNewlineCharacterSet]]
480 if ([head hasPrefix:@"<?xml"] ||
481 [head hasPrefix:@"<!doctype rss"])
483 else if ([head hasPrefix:@"<!doctype html"] ||
484 [head hasPrefix:@"<html"] ||
485 [head hasPrefix:@"<head"])
487 else if ([head rangeOfString:@"<base"].length ||
488 [head rangeOfString:@"<body"].length ||
489 [head rangeOfString:@"<script"].length ||
490 [head rangeOfString:@"<style"].length ||
491 [head rangeOfString:@"<a href"].length)
493 else if ([head rangeOfString:@"<channel"].length ||
494 [head rangeOfString:@"<generator"].length ||
495 [head rangeOfString:@"<description"].length ||
496 [head rangeOfString:@"<content"].length ||
497 [head rangeOfString:@"<feed"].length ||
498 [head rangeOfString:@"<entry"].length)
503 char *body2 = strdup ([body cStringUsingEncoding:NSISOLatin1StringEncoding]);
506 case HTML: strip_html (body2); break;
507 case RSS: strip_rss (body2); break;
509 default: abort(); break;
517 textclient_getc (text_data *d)
519 if (!d->fp || !*d->fp) {
527 d->buf = date_string();
530 if (!d->literal || !*d->literal)
532 d->buf = (char *) malloc (strlen (d->literal) + 3);
533 strcpy (d->buf, d->literal);
534 strcat (d->buf, "\n");
535 strip_backslashes (d->buf);
539 if (!d->url || !*d->url)
541 d->buf = url_string (d->url);
547 wrap_text (d->buf, d->columns);
551 if (!d->fp || !*d->fp)
554 unsigned char c = (unsigned char) *d->fp++;
560 textclient_putc (text_data *d, XKeyEvent *k)
567 textclient_reshape (text_data *d,
568 int pix_w, int pix_h,
569 int char_w, int char_h)
572 rewrap_text (d->buf, d->columns);
575 #endif /* USE_IPHONE -- whole file */