Overview
Comment: | add tenki |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
ae8a97d78350b9ec4506826354c461e1 |
User & Date: | lexi on 2019-04-30 23:26:34 |
Other Links: | manifest | tags |
Context
2019-05-01
| ||
01:03 | refactor tenki check-in: 70991b55c3 user: lexi tags: trunk | |
2019-04-30
| ||
23:26 | add tenki check-in: ae8a97d783 user: lexi tags: trunk | |
2019-04-23
| ||
02:01 | fix bug where bgrd also scooped stderr, thereby breaking my `surf` setup in neovim check-in: 7a21b1f19e user: lexi tags: trunk | |
Changes
Added tenki/makefile version [d561bb7339].
1 +tenki: tenki.cc 2 + $(CC) -Ofast tenki.cc -o tenki -lssl
Added tenki/tenki.cc version [eaa2ec3cc1].
1 +#include <stdio.h> 2 +#include <stdlib.h> 3 +#include <unistd.h> 4 +#include <string.h> 5 +#include <sys/socket.h> 6 +#include <netinet/in.h> 7 +#include <netdb.h> 8 +#include <stdint.h> 9 +#include <openssl/ssl.h> 10 + 11 +#define ds_apikey "a6aa0337fafd8b8fcfafe84a403ed24f" 12 +#define ds_lang "ja" 13 +#define ds_exclude "minutely,hourly,daily,alerts,flags" 14 + 15 +#define req "GET /forecast/" 16 +#define http "?exclude=" ds_exclude "&lang=" ds_lang " HTTP/1.1\r\nHost: api.darksky.net\r\n\r\n" 17 + 18 +#define len(x) (sizeof(x)/sizeof(*x)) 19 +#define start req ds_apikey "/" 20 +#define min(x,y) (x>y?y:x) 21 + 22 +SSL_CTX* sc; 23 +bool head(size_t* len, char* txt, char** body) { 24 + char* hend = strstr(txt, "\r\n\r\n"); 25 + if (hend == nullptr) return false; 26 + #define ctlhead "\r\nContent-Length:" 27 + char* ctl = strstr(txt, ctlhead); 28 + if (ctl == nullptr or ctl > hend) return false; 29 + ctl += len(ctlhead); 30 + *len = 0; 31 + while(*ctl != '\r') { 32 + if (*ctl >= '0' and *ctl <= '9') *len*=10,*len+=*ctl-'0'; 33 + ++ctl; 34 + } 35 + *len+=(hend-txt)+4; 36 + *body = hend+4; 37 + return true; 38 +} 39 +size_t get(char* resp, size_t max, const char* msg, size_t total, char** body){ 40 + 41 + int port = 443; 42 + const char* host = "api.darksky.net"; 43 + //printf("req: %s\n", msg); 44 + struct hostent* api; 45 + struct sockaddr_in api_addr; 46 + int sockfd, bytes; 47 + size_t sent, recd; 48 + 49 + sockfd = socket(AF_INET, SOCK_STREAM, 0); 50 + if (sockfd < 0) exit(2); 51 + SSL *con = SSL_new(sc); 52 + SSL_set_fd(con, sockfd); 53 + 54 + //printf("getting host\n"); 55 + api = gethostbyname(host); 56 + if (api == nullptr) exit(3); 57 + 58 + memset(&api_addr, 0, sizeof(api_addr)); 59 + api_addr.sin_family = AF_INET; 60 + api_addr.sin_port = htons(port); 61 + memcpy(&api_addr.sin_addr.s_addr, api -> h_addr, api -> h_length); 62 + 63 + //printf("connecting\n"); 64 + if (connect(sockfd,(struct sockaddr*)&api_addr,sizeof(api_addr))) 65 + exit(4); 66 + int err = SSL_connect(con); 67 + 68 + 69 + sent = 0; 70 +// printf("writing\n"); 71 + do { 72 + // printf("writing byte %x\n",*(msg+sent)); 73 + bytes = SSL_write(con,msg+sent,total-sent); 74 + if (bytes < 0) exit (4); 75 + if (bytes == 0) break; 76 + ++sent; 77 + } while (sent < total); 78 +// printf("receiving\n"); 79 + recd = 0; 80 + size_t resplen = max; 81 + bool rhead = false; 82 + do { 83 + // printf("\n\nreading - addr %x; max %d\n",resp+recd,resplen-recd); 84 + bytes = SSL_read(con,resp+recd,min(resplen-recd,15000)); 85 + if (bytes < 0) exit (5); 86 + if (bytes == 0) break; 87 + if(!rhead)rhead=head(&resplen, resp, body); 88 + // printf("got %d bytes: %s", bytes, resp+recd); 89 + recd += bytes; 90 + } while (recd < resplen - 1); 91 + 92 + close(sockfd); 93 + SSL_shutdown(con); 94 + 95 + return recd; 96 +} 97 +size_t pos(char* msg, const char* lat, const char* lng) { 98 + char* cur = msg + len(start) - 1; 99 + size_t latl = strlen(lat); 100 + size_t longl = strlen(lng); 101 + strcpy(cur, lat); cur += latl; 102 + *cur++=','; 103 + strcpy(cur, lng); cur += longl; 104 + strcpy(cur, http); cur += len(http); 105 + *cur--=0; 106 + return cur-msg; 107 +} 108 +bool estrc(size_t sz, const char* str1, const char* str2) { 109 + for(size_t i = 0;i<sz;++i) { 110 + if(*str1 == *str2) { 111 + ++str1; ++str2; 112 + } else if (*str2 =='\\') { 113 + ++str2; 114 + if(*str1 == *str2) { 115 + ++str1; ++str2; 116 + } else return false; 117 + } else { 118 + return false; 119 + } 120 + } 121 + return true; 122 +} 123 +struct pstr { 124 + size_t sz; 125 + const char* a; 126 +}; 127 +union jsv { 128 + float f; 129 + pstr s; 130 +}; 131 +/* void printkey(const char* key) { 132 + size_t len = *key++; 133 + for (size_t i = 0; i<len; ++i) { 134 + printf("/%.*s",*key,key+1); 135 + key += *key + 1; 136 + }; 137 + printf("\n"); 138 +} */ 139 +void pplt(const char* str, const char* const end, const char** keys, const char** const keyend, jsv* values) { 140 + // initialize parser state 141 + size_t depth = 0; 142 + const char* fld = nullptr; 143 + const char* path[32]; 144 + path[0] = nullptr; 145 + 146 + while(*str++!='{'); //cheat 147 + init: { 148 + if (keys > keyend) return; 149 + if (str > end) return; 150 + /* print_path: { 151 + printf("current path: /"); 152 + for (size_t i = 0; i < depth; ++i) { 153 + printf("%.*s/",strchr(path[i],'"')-path[i],path[i]); 154 + } 155 + printf("\n"); 156 + printf("current key: "); 157 + printkey(*keys); 158 + } */ 159 + parse_char: { 160 + switch(*str++) { 161 + case ' ': case '\t': case '\n': 162 + goto parse_char; 163 + case '"': 164 + path[depth] = str; 165 + goto path_find_quote_end; 166 + case '}': 167 + if (depth == 0) return; 168 + depth--; 169 + while(*str!=',') ++str; ++str; 170 + goto init; 171 + default: 172 + printf("found illegal char %c\n", *(str-1)); 173 + exit(6); 174 + } 175 + } 176 + path_find_quote_end: { 177 + switch (*str++) { 178 + case '\\': ++str; goto path_find_quote_end; 179 + case '"': goto read_value; 180 + default: goto path_find_quote_end; 181 + } 182 + } 183 + read_value: { 184 + if (*str++!=':') { 185 + printf("illegal char\n"); 186 + exit(6); 187 + } 188 + bool iskey; 189 + const char* key = *keys; 190 + jsv* value; 191 + if (depth + 1 != *key++) { 192 + iskey = false; 193 + goto comp; 194 + } else for (size_t i = 0; i <= depth; ++i) { 195 + if (estrc(*key, key+1, path[i])) { 196 + key += *key + 1; 197 + } else { 198 + iskey = false; 199 + goto comp; 200 + } 201 + } 202 + iskey = true; 203 + value = values; 204 + ++keys; ++values; 205 + comp: if (*str == '{') { 206 + ++depth; ++str; 207 + goto init; 208 + } else if (*str == '"') { 209 + goto copy_str_value; 210 + } else if (*str == '[') { 211 + goto skip_array; 212 + } else if ((*str >= '0' and *str <= '9') or *str=='-') { 213 + goto copy_int_value; 214 + } else { 215 + printf("illegal character found in value %c\n", *str); 216 + exit(6); 217 + } 218 + 219 + copy_str_value: { 220 + fld = ++str; 221 + while (*++str != '"') if (*str == '\\') { ++str; continue; } 222 + if (iskey) { 223 + value -> s.sz = str - fld; 224 + value -> s.a = fld; 225 + } 226 + while (*++str != ','); 227 + ++str; goto init; 228 + } 229 + skip_array: { 230 + size_t ard = 0; 231 + skip_loop: switch (*++str) { 232 + case '{': case '[': ++ard; goto skip_loop; 233 + case '"': goto skip_str; 234 + case '}': if (ard == 0) { 235 + printf("bad json\n"); 236 + exit(7); 237 + } else { --ard; goto skip_loop; } 238 + case ']': if (ard == 0) { 239 + if (*++str == ',') { ++str; goto init; } 240 + else goto init; 241 + } else { --ard; } 242 + default: goto skip_loop; 243 + } 244 + skip_str: while (*str != '"') if (*str == '\\') { str += 2; continue; } else ++str; goto skip_loop; 245 + } 246 + copy_int_value: { 247 + if (!iskey) { 248 + while(*str++!=',') if (str > end) return; 249 + goto init; 250 + } 251 + value -> f = 0; 252 + bool neg; 253 + if (*str == '-') { neg=true; ++str; } else neg=false; 254 + while(*str >= '0' and *str <= '9') { 255 + value -> f *= 10; 256 + value -> f += *str - '0'; 257 + ++str; 258 + } 259 + if(*str == '.') { 260 + float fac = 0.1; 261 + float val = 0; 262 + while(*++str >= '0' and *str <= '9') { 263 + val += (((float)(*str - '0')) * fac); 264 + fac *= 0.1; 265 + } 266 + value -> f += val; 267 + } 268 + if(neg) value -> f *= -1; 269 + if (*str == ',') { 270 + ++str; goto init; 271 + } else { 272 + printf("illegal character %.*s\n",15,str); 273 + exit(6); 274 + } 275 + } 276 + } 277 + } 278 +} 279 +int main(int argc, char** argv) { 280 + if (argc != 3) exit(1); 281 + SSL_load_error_strings(); 282 + SSL_library_init(); 283 + sc = SSL_CTX_new(SSLv23_client_method()); 284 + 285 + char msg[256] = start; 286 + char resp[32368]; 287 + char* body; 288 + 289 + // name the fields to extract from the json stream 290 + // note: MUST BE LISTED IN ORDER THEY APPEAR 291 + // paths are encoded as sequences of pstrings 292 + // initial value indicates depth of path 293 + // TODO: performance worth lack of flexibility? 294 + const char* paths[] = { 295 + "\2\x9""currently\7summary", 296 + "\2\x9""currently\xfprecipIntensity", 297 + "\2\x9""currently\xbtemperature", 298 + "\2\x9""currently\x9windspeed", 299 + }; 300 + 301 + size_t msgsz = pos(msg, argv[1], argv[2]); 302 + //msgsz = size of transmit 303 + //for (;;) { 304 + size_t rspsz = get(resp, sizeof(resp), msg, msgsz, &body); 305 + // if paths contains the keys, this contains the values 306 + jsv values[len(paths)]; 307 + 308 + parse: { 309 + pplt(body, body + (rspsz - (resp - body)), paths, paths+len(paths), values); 310 + 311 + pstr summary = values[0].s; 312 + float pcpint = values[1].f, 313 + temp = values[2].f, 314 + windspd = values[3].f; 315 + 316 + printf("%.1f° %%{F#ff9bbb}%.*s\n", 317 + summary.sz, 318 + summary.a, 319 + temp); 320 + } 321 + // dark sky's API allows us to make 1000 free requests a day. 322 + // let's try not to get too near that. 323 + // hours in day 24 - minutes in day 24 * 60 = 1440 324 + // so once a minute is almost half again too many 325 + // 1440 / 2 = 720 leaving reasonable refresh time without 326 + // going too near the limit. so we aim for one update 327 + // every two minutes. 328 + // sleep(60 * 2); 329 +// } 330 + 331 + return 0; 332 +}