1 /* xscreensaver, Copyright (c) 2012-2014 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;
43 textclient_open (Display *dpy)
45 text_data *d = (text_data *) calloc (1, sizeof (*d));
48 fprintf (stderr, "%s: textclient: init\n", progname);
51 char *s = get_string_resource (dpy, "textMode", "TextMode");
52 if (!s || !*s || !strcasecmp (s, "date") || !strcmp (s, "0"))
54 else if (!strcasecmp (s, "literal") || !strcmp (s, "1"))
56 else if (!strcasecmp (s, "url") || !strcmp (s, "3"))
61 d->literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
62 d->url = get_string_resource (dpy, "textURL", "TextURL");
69 textclient_close (text_data *d)
72 fprintf (stderr, "%s: textclient: free\n", progname);
75 if (d->buf) free (d->buf);
76 if (d->literal) free (d->literal);
77 if (d->url) free (d->url);
85 UIDevice *dd = [UIDevice currentDevice];
86 NSString *name = [dd name]; // My iPhone
87 NSString *model = [dd model]; // iPad
88 // NSString *system = [dd systemName]; // iPhone OS
89 NSString *vers = [dd systemVersion]; // 5.0
92 localizedStringFromDate:[NSDate date]
93 dateStyle: NSDateFormatterMediumStyle
94 timeStyle: NSDateFormatterMediumStyle];
97 NSString *result = name;
98 result = [result stringByAppendingString: nl];
99 result = [result stringByAppendingString: model];
100 // result = [result stringByAppendingString: nl];
101 // result = [result stringByAppendingString: system];
102 result = [result stringByAppendingString: @" "];
103 result = [result stringByAppendingString: vers];
104 result = [result stringByAppendingString: nl];
105 result = [result stringByAppendingString: nl];
106 result = [result stringByAppendingString: date];
107 result = [result stringByAppendingString: nl];
108 result = [result stringByAppendingString: nl];
109 return strdup ([result cStringUsingEncoding:NSISOLatin1StringEncoding]);
114 strip_html (char *html)
122 for (char *in = html; *in; in++) {
124 if (!strncmp (in, "-->", 3)) {
136 } else if (*in == '<') {
139 if (!strncmp (in, "<!--", 4)) {
142 } else if (!strncasecmp (in, "<BR", 3)) {
146 } else if (!strncasecmp (in, "<P", 2)) {
147 if (nl < 2) { *out++ = '\n'; nl++; }
148 if (nl < 2) { *out++ = '\n'; nl++; }
151 } else if (*in == '&') {
155 if (!strncmp (in, "&", 4)) ss = "&";
156 else if (!strncmp (in, "<", 3)) ss = "<";
157 else if (!strncmp (in, ">", 3)) ss = ">";
158 else if (!strncmp (in, " ", 5)) ss = " ";
160 else if (!strncmp (in, "Æ", 6)) ss = "AE";
161 else if (!strncmp (in, "æ", 6)) ss = "ae";
162 else if (!strncmp (in, "&bdquo", 6)) ss = "\"";
163 else if (!strncmp (in, "¦", 7)) ss = "|";
164 else if (!strncmp (in, "&bull", 5)) ss = "*";
165 else if (!strncmp (in, "&circ", 5)) ss = "^";
166 else if (!strncmp (in, "&cong", 5)) ss = "=~";
167 else if (!strncmp (in, "©", 5)) ss = "(c)";
168 else if (!strncmp (in, "¤", 7)) ss = "$";
169 else if (!strncmp (in, "°", 4)) ss = ".";
170 else if (!strncmp (in, "÷", 7)) ss = "/";
171 else if (!strncmp (in, "&empty", 6)) ss = "0";
172 else if (!strncmp (in, "&emsp", 5)) ss = " ";
173 else if (!strncmp (in, "&ensp", 5)) ss = " ";
174 else if (!strncmp (in, "&equiv", 6)) ss = "==";
175 else if (!strncmp (in, "½", 7)) ss = "1/2";
176 else if (!strncmp (in, "¼", 7)) ss = "1/4";
177 else if (!strncmp (in, "¾", 7)) ss = "3/4";
178 else if (!strncmp (in, "&frasl", 6)) ss = "/";
179 else if (!strncmp (in, "&ge", 3)) ss = ">=";
180 else if (!strncmp (in, "&hArr", 5)) ss = "<=>";
181 else if (!strncmp (in, "&harr", 5)) ss = "<->";
182 else if (!strncmp (in, "&hellip", 7)) ss = "...";
183 else if (!strncmp (in, "¿", 7)) ss = "?";
184 else if (!strncmp (in, "&lArr", 5)) ss = "<=";
185 else if (!strncmp (in, "&lang", 5)) ss = "<";
186 else if (!strncmp (in, "«", 6)) ss = "<<";
187 else if (!strncmp (in, "&larr", 5)) ss = "<-";
188 else if (!strncmp (in, "&ldquo", 6)) ss = "\"";
189 else if (!strncmp (in, "&le", 3)) ss = "<=";
190 else if (!strncmp (in, "&lowast", 7)) ss = "*";
191 else if (!strncmp (in, "&loz", 4)) ss = "<>";
192 else if (!strncmp (in, "&lsaquo", 7)) ss = "<";
193 else if (!strncmp (in, "&lsquo", 6)) ss = "`";
194 else if (!strncmp (in, "¯", 5)) ss = "'";
195 else if (!strncmp (in, "&mdash", 6)) ss = "--";
196 else if (!strncmp (in, "µ", 6)) ss = "u";
197 else if (!strncmp (in, "·", 7)) ss = ".";
198 else if (!strncmp (in, "&minus", 6)) ss = "-";
199 else if (!strncmp (in, "&ndash", 6)) ss = "-";
200 else if (!strncmp (in, "&ne", 3)) ss = "!=";
201 else if (!strncmp (in, "¬", 4)) ss = "!";
202 else if (!strncmp (in, "&OElig", 6)) ss = "OE";
203 else if (!strncmp (in, "&oelig", 6)) ss = "oe";
204 else if (!strncmp (in, "ª", 5)) ss = "_";
205 else if (!strncmp (in, "º", 5)) ss = "_";
206 else if (!strncmp (in, "¶", 5)) ss = "PP";
207 else if (!strncmp (in, "±", 7)) ss = "+/-";
208 else if (!strncmp (in, "£", 6)) ss = "#";
209 else if (!strncmp (in, "&prime", 6)) ss = "'";
210 else if (!strncmp (in, """, 5)) ss = "\"";
211 else if (!strncmp (in, "&rArr", 5)) ss = "=>";
212 else if (!strncmp (in, "&rang", 5)) ss = ">";
213 else if (!strncmp (in, "»", 6)) ss = ">>";
214 else if (!strncmp (in, "&rarr", 5)) ss = "->";
215 else if (!strncmp (in, "&rdquo", 6)) ss = "\"";
216 else if (!strncmp (in, "®", 4)) ss = "(r)";
217 else if (!strncmp (in, "&rsaquo", 7)) ss = ">";
218 else if (!strncmp (in, "&rsquo", 6)) ss = "'";
219 else if (!strncmp (in, "&sbquo", 6)) ss = "'";
220 else if (!strncmp (in, "§", 5)) ss = "SS";
221 else if (!strncmp (in, "­", 4)) ss = "";
222 else if (!strncmp (in, "&sim", 4)) ss = "~";
223 else if (!strncmp (in, "¹", 5)) ss = "[1]";
224 else if (!strncmp (in, "²", 5)) ss = "[2]";
225 else if (!strncmp (in, "³", 5)) ss = "[3]";
226 else if (!strncmp (in, "ß", 6)) ss = "B";
227 else if (!strncmp (in, "&thinsp", 7)) ss = " ";
228 else if (!strncmp (in, "þ", 6)) ss = "|";
229 else if (!strncmp (in, "&tilde", 6)) ss = "!";
230 else if (!strncmp (in, "×", 6)) ss = "x";
231 else if (!strncmp (in, "&trade", 6)) ss = "[tm]";
232 else if (!strncmp (in, "¨", 4)) ss = ":";
233 else if (!strncmp (in, "¥", 4)) ss = "Y";
238 } else if (!strncmp (in, "&#", 2)) { // A
240 for (char *in2 = in+2; *in2 >= '0' && *in2 <= '9'; in2++)
241 i = (i * 10) + (*in2 - '0');
242 *out = (i > 255 ? '?' : i);
243 } else if (!strncmp (in, "&#x", 3)) { // A
245 for (char *in2 = in+3;
246 ((*in2 >= '0' && *in2 <= '9') ||
247 (*in2 >= 'A' && *in2 <= 'F') ||
248 (*in2 >= 'a' && *in2 <= 'f'));
250 i = (i * 16) + (*in2 >= 'a' ? *in2 - 'a' + 16 :
251 *in2 >= 'A' ? *in2 - 'A' + 16 :
253 *out = (i > 255 ? '?' : i);
255 *out++ = in[1]; // first character of entity, e.g. é.
257 } else if (*in == ' ' || *in == '\t' || *in == '\r' || *in == '\n') {
258 if (!white && out != html)
272 copy_rss_field (const char *s)
275 while (*s && *s != '>') // Skip forward to >
280 if (!strncmp (s, "<![CDATA[", 9)) { // CDATA quoting
282 char *e = strstr (s, "]]");
284 unsigned long L = strlen (s);
285 char *s2 = (char *) malloc (L+1);
289 } else { // Entity-encoded.
291 for (s2 = s; *s2 && *s2 != '<'; s2++) // Terminate at <
293 char *s3 = (char *) malloc (s2 - s + 1);
295 memcpy (s3, s, s2-s);
304 pick_rss_field (const char *a, const char *b, const char *c, const char *d)
306 // Pick the longest of the fields.
307 char *a2 = copy_rss_field (a);
308 char *b2 = copy_rss_field (b);
309 char *c2 = copy_rss_field (c);
310 char *d2 = copy_rss_field (d);
311 unsigned long al = a2 ? strlen(a2) : 0;
312 unsigned long bl = b2 ? strlen(b2) : 0;
313 unsigned long cl = c2 ? strlen(c2) : 0;
314 unsigned long dl = d2 ? strlen(d2) : 0;
317 if (al > bl && al > cl && al > dl) ret = a2;
318 else if (bl > al && bl > cl && bl > dl) ret = b2;
319 else if (cl > al && cl > bl && cl > dl) ret = c2;
321 if (a2 && a2 != ret) free (a2);
322 if (b2 && b2 != ret) free (b2);
323 if (c2 && c2 != ret) free (c2);
324 if (d2 && d2 != ret) free (d2);
330 strip_rss (char *rss)
333 const char *a = 0, *b = 0, *c = 0, *d = 0, *t = 0;
337 for (char *in = rss; *in; in++) {
339 if (!strncasecmp (in, "<item", 5) || // New item, dump.
340 !strncasecmp (in, "<entry", 6)) {
343 char *title = copy_rss_field (t);
344 char *body = pick_rss_field (a, b, c, d);
346 a = b = c = d = t = 0;
348 if (title && body && !strcmp (title, body)) {
357 strcpy (out, "\n\n");
369 } else if (head) { // still before first <item>
371 } else if (!strncasecmp (in, "<title", 6)) {
373 } else if (!strncasecmp (in, "<summary", 8)) {
375 } else if (!strncasecmp (in, "<description", 12)) {
377 } else if (!strncasecmp (in, "<content:encoded", 16)) {
379 } else if (!strncasecmp (in, "<content", 8)) {
385 if (! done) { // Finish off the final item.
390 // Now decode it a second time.
396 wrap_text (char *body, int columns, int max_lines)
398 int col = 0, last_col = 0;
399 char *last_space = 0;
402 for (char *p = body; *p; p++) {
403 if (*p == '\r' || *p == '\n' || *p == ' ' || *p == '\t') {
404 if (col > columns && last_space) {
406 col = col - last_col;
411 if (*p == '\r' || *p == '\n') {
416 if (max_lines && lines >= max_lines)
429 rewrap_text (char *body, int columns)
432 for (char *p = body; *p; p++) {
440 wrap_text (body, columns, 0);
446 strip_backslashes (char *s)
449 for (char *in = s; *in; in++) {
452 if (*in == 'n') *out++ = '\n';
453 else if (*in == 'r') *out++ = '\r';
454 else if (*in == 't') *out++ = '\t';
465 url_string (const char *url)
468 [NSURL URLWithString:
469 [NSString stringWithCString: url
470 encoding:NSISOLatin1StringEncoding]];
472 [NSString stringWithContentsOfURL: nsurl
473 encoding: NSISOLatin1StringEncoding
478 enum { RSS, HTML, TEXT } type;
480 // Only search the first 1/2 K of the document while determining type.
482 unsigned long L = [body length];
483 if (L > 512) L = 512;
484 NSString *head = [[[body substringToIndex: L]
485 stringByTrimmingCharactersInSet:
486 [NSCharacterSet whitespaceAndNewlineCharacterSet]]
488 if ([head hasPrefix:@"<?xml"] ||
489 [head hasPrefix:@"<!doctype rss"])
491 else if ([head hasPrefix:@"<!doctype html"] ||
492 [head hasPrefix:@"<html"] ||
493 [head hasPrefix:@"<head"])
495 else if ([head rangeOfString:@"<base"].length ||
496 [head rangeOfString:@"<body"].length ||
497 [head rangeOfString:@"<script"].length ||
498 [head rangeOfString:@"<style"].length ||
499 [head rangeOfString:@"<a href"].length)
501 else if ([head rangeOfString:@"<channel"].length ||
502 [head rangeOfString:@"<generator"].length ||
503 [head rangeOfString:@"<description"].length ||
504 [head rangeOfString:@"<content"].length ||
505 [head rangeOfString:@"<feed"].length ||
506 [head rangeOfString:@"<entry"].length)
511 char *body2 = strdup ([body cStringUsingEncoding:NSISOLatin1StringEncoding]);
514 case HTML: strip_html (body2); break;
515 case RSS: strip_rss (body2); break;
517 default: abort(); break;
525 textclient_getc (text_data *d)
527 if (!d->fp || !*d->fp) {
535 d->buf = date_string();
538 if (!d->literal || !*d->literal)
540 d->buf = (char *) malloc (strlen (d->literal) + 3);
541 strcpy (d->buf, d->literal);
542 strcat (d->buf, "\n");
543 strip_backslashes (d->buf);
547 if (!d->url || !*d->url)
549 d->buf = url_string (d->url);
555 wrap_text (d->buf, d->columns, d->max_lines);
559 if (!d->fp || !*d->fp)
562 unsigned char c = (unsigned char) *d->fp++;
568 textclient_putc (text_data *d, XKeyEvent *k)
575 textclient_reshape (text_data *d,
576 int pix_w, int pix_h,
577 int char_w, int char_h,
581 d->max_lines = max_lines;
582 rewrap_text (d->buf, d->columns);
585 #endif /* USE_IPHONE -- whole file */