util  Check-in [ae8a97d783]

Overview
Comment:add tenki
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: ae8a97d78350b9ec4506826354c461e1d4aa9e1a0298912e6266fb01ce91cc30
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  +}