void do_launch_urls(void) { HGLOBAL clipdata; char *s = NULL, *t = NULL, *p, *q, *r, *e; int len, ret, in_url = 0, n = 0; if (!OpenClipboard(NULL)) { goto error; /* unable to read clipboard */ } clipdata = GetClipboardData(CF_TEXT); CloseClipboard(); if (!clipdata) { goto error; /* clipboard contains no text */ } s = GlobalLock(clipdata); if (!s) { goto error; /* unable to lock clipboard memory */ } /* * Now strip (some) whitespace from the URL text. * In a future version this might be made configurable. */ len = strlen(s); t = malloc(len+8); /* leading "http://" plus trailing \0 */ if (!t) { GlobalUnlock(s); goto error; /* out of memory */ } p = s; q = t; /* Strip leading whitespace. */ while (p[0] && our_isspace(p[0])) p++; /* Find any newlines, and close up any white-space around them. */ while ((r = strpbrk(p, "\r\n"))) { char *rr = r; /* Strip whitespace before newline(s) */ while (our_isspace(rr[0])) rr--; while (p <= rr) *q++ = *p++; /* Strip newline(s) and following whitespace */ p = r; while (p[0] && our_isspace(p[0])) p++; } /* Strip any trailing whitespace */ r = p + strlen(p); while (r > p && our_isspace(r[-1])) r--; while (p < r) *q++ = *p++; *q = '\0'; GlobalUnlock(s); /* * Now we have whitespace-filtered text in t. * First look for <>-delimited strings per RFC3986, and try to * launch any we find as URLs. */ q = t; while ((e = strpbrk(q, "<>"))) { switch (*e) { case '<': q = e+1; in_url = 1; break; case '>': if (in_url) { /* deal with RFC1738 <URL:...> */ if (strncmp(q, "URL:", 4) == 0) q += 4; /* Remove whitespace between < / <URL: / > and URL. */ while (our_isspace(*q)) q++; { char *ee = e-1; while (our_isspace(*ee)) ee--; ee[1] = '\0'; } /* We may as well allow non-RFC <*****@*****.**> and * indeed <URL:www.example.com>. */ (void) launch_url(q); n++; in_url = 0; } q = e+1; break; } } if (!n) { /* Didn't find any URLs by that method. Try parsing the string * as a whole as one. */ ret = launch_url(t); } free(t); error: /* * Not entirely sure what we should do in case of error here. * Perhaps a simple beep might be suitable. Then again, `out of * memory' is a bit scarier. FIXME: should probably do * different error handling depending on context. */ ; }
/* Stand alone implementation of sscanf. We used to call libc's vsscanf while * trying to isolate errno (i#238), but these days sscanf calls malloc (i#762). * Therefore, we roll our own. */ int our_vsscanf(const char *str, const char *fmt, va_list ap) { int num_parsed = 0; const char *fp = fmt; const char *sp = str; int c; while (*fp != '\0' && *sp != '\0') { specifer_t spec = SPEC_INT; int_sz_t int_size = SZ_INT; uint base = 10; bool is_signed = false; bool is_ignored = false; uint width = 0; /* Handle literal characters and spaces up front. */ c = *fp++; if (our_isspace(c)) { /* Space means consume any number of spaces. */ while (our_isspace(*sp)) { sp++; } continue; } else if (c != '%') { /* Literal, so check mismatch. */ if (c != *sp) return num_parsed; sp++; continue; } /* Parse the format specifier. */ ASSERT(c == '%'); while (true) { c = *fp++; switch (c) { /* Modifiers, should all continue the loop. */ case 'l': ASSERT(int_size != SZ_LONGLONG && "too many longs"); if (int_size == SZ_LONG) int_size = SZ_LONGLONG; else int_size = SZ_LONG; continue; case 'h': int_size = SZ_SHORT; continue; case '*': is_ignored = true; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* We honor the specified width for strings to prevent buffer * overruns, but we don't honor it for integers. Honoring the * width for integers would require our own integer parser. */ width = width * 10 + c - '0'; continue; /* Specifiers, should all break the loop. */ case 'u': spec = SPEC_INT; is_signed = false; goto spec_done; case 'd': spec = SPEC_INT; is_signed = true; goto spec_done; case 'x': spec = SPEC_INT; is_signed = false; base = 16; goto spec_done; case 'p': int_size = SZ_PTR; spec = SPEC_INT; is_signed = false; base = 16; goto spec_done; case 'c': spec = SPEC_CHAR; goto spec_done; case 's': spec = SPEC_STRING; goto spec_done; default: ASSERT(false && "unknown specifier"); return num_parsed; } } spec_done: /* Parse the string. */ switch (spec) { case SPEC_CHAR: if (!is_ignored) { *va_arg(ap, char*) = *sp; } sp++; break; case SPEC_STRING: if (is_ignored) { while (*sp != '\0' && !our_isspace(*sp)) { sp++; } } else { char *str_out = va_arg(ap, char*); if (width > 0) { int i = 0; while (i < width && *sp != '\0' && !our_isspace(*sp)) { *str_out++ = *sp++; i++; } /* Spec says only null terminate if we hit width. */ if (i < width) *str_out = '\0'; } else { while (*sp != '\0' && !our_isspace(*sp)) { *str_out++ = *sp++; } *str_out = '\0'; } } break; case SPEC_INT: { uint64 res; sp = parse_int(sp, &res, base, width, is_signed); if (sp == NULL) return num_parsed; if (!is_ignored) { if (int_size == SZ_SHORT) *va_arg(ap, short *) = (short)res; else if (int_size == SZ_INT) *va_arg(ap, int *) = (int)res; else if (int_size == SZ_LONG) *va_arg(ap, long *) = (long)res; else if (int_size == SZ_LONGLONG) *va_arg(ap, long long *) = (long long)res; else ASSERT_NOT_REACHED(); } break; } default: /* Format parsing code above should return an error earlier. */ ASSERT_NOT_REACHED(); } if (!is_ignored) num_parsed++; } return num_parsed; }