A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 682 lines 21 kB view raw
1/* 2 * misc.c: Miscellaneous helpful functions. 3 */ 4 5#include <assert.h> 6#include <ctype.h> 7#ifdef NO_TGMATH_H 8# include <math.h> 9#else 10# include <tgmath.h> 11#endif 12#include <stdlib.h> 13#include <string.h> 14#include <stdio.h> 15 16#include "puzzles.h" 17 18char MOVE_UI_UPDATE[] = ""; 19char MOVE_NO_EFFECT[] = ""; 20char MOVE_UNUSED[] = ""; 21 22void free_cfg(config_item *cfg) 23{ 24 config_item *i; 25 26 for (i = cfg; i->type != C_END; i++) 27 if (i->type == C_STRING) 28 sfree(i->u.string.sval); 29 sfree(cfg); 30} 31 32void free_keys(key_label *keys, int nkeys) 33{ 34 int i; 35 36 for(i = 0; i < nkeys; i++) 37 sfree(keys[i].label); 38 sfree(keys); 39} 40 41/* 42 * The Mines (among others) game descriptions contain the location of every 43 * mine, and can therefore be used to cheat. 44 * 45 * It would be pointless to attempt to _prevent_ this form of 46 * cheating by encrypting the description, since Mines is 47 * open-source so anyone can find out the encryption key. However, 48 * I think it is worth doing a bit of gentle obfuscation to prevent 49 * _accidental_ spoilers: if you happened to note that the game ID 50 * starts with an F, for example, you might be unable to put the 51 * knowledge of those mines out of your mind while playing. So, 52 * just as discussions of film endings are rot13ed to avoid 53 * spoiling it for people who don't want to be told, we apply a 54 * keyless, reversible, but visually completely obfuscatory masking 55 * function to the mine bitmap. 56 */ 57void obfuscate_bitmap(unsigned char *bmp, int bits, bool decode) 58{ 59 int bytes, firsthalf, secondhalf; 60 struct step { 61 unsigned char *seedstart; 62 int seedlen; 63 unsigned char *targetstart; 64 int targetlen; 65 } steps[2]; 66 int i, j; 67 68 /* 69 * My obfuscation algorithm is similar in concept to the OAEP 70 * encoding used in some forms of RSA. Here's a specification 71 * of it: 72 * 73 * + We have a `masking function' which constructs a stream of 74 * pseudorandom bytes from a seed of some number of input 75 * bytes. 76 * 77 * + We pad out our input bit stream to a whole number of 78 * bytes by adding up to 7 zero bits on the end. (In fact 79 * the bitmap passed as input to this function will already 80 * have had this done in practice.) 81 * 82 * + We divide the _byte_ stream exactly in half, rounding the 83 * half-way position _down_. So an 81-bit input string, for 84 * example, rounds up to 88 bits or 11 bytes, and then 85 * dividing by two gives 5 bytes in the first half and 6 in 86 * the second half. 87 * 88 * + We generate a mask from the second half of the bytes, and 89 * XOR it over the first half. 90 * 91 * + We generate a mask from the (encoded) first half of the 92 * bytes, and XOR it over the second half. Any null bits at 93 * the end which were added as padding are cleared back to 94 * zero even if this operation would have made them nonzero. 95 * 96 * To de-obfuscate, the steps are precisely the same except 97 * that the final two are reversed. 98 * 99 * Finally, our masking function. Given an input seed string of 100 * bytes, the output mask consists of concatenating the SHA-1 101 * hashes of the seed string and successive decimal integers, 102 * starting from 0. 103 */ 104 105 bytes = (bits + 7) / 8; 106 firsthalf = bytes / 2; 107 secondhalf = bytes - firsthalf; 108 109 steps[decode ? 1 : 0].seedstart = bmp + firsthalf; 110 steps[decode ? 1 : 0].seedlen = secondhalf; 111 steps[decode ? 1 : 0].targetstart = bmp; 112 steps[decode ? 1 : 0].targetlen = firsthalf; 113 114 steps[decode ? 0 : 1].seedstart = bmp; 115 steps[decode ? 0 : 1].seedlen = firsthalf; 116 steps[decode ? 0 : 1].targetstart = bmp + firsthalf; 117 steps[decode ? 0 : 1].targetlen = secondhalf; 118 119 for (i = 0; i < 2; i++) { 120 SHA_State base, final; 121 unsigned char digest[20]; 122 char numberbuf[80]; 123 int digestpos = 20, counter = 0; 124 125 SHA_Init(&base); 126 SHA_Bytes(&base, steps[i].seedstart, steps[i].seedlen); 127 128 for (j = 0; j < steps[i].targetlen; j++) { 129 if (digestpos >= 20) { 130 sprintf(numberbuf, "%d", counter++); 131 final = base; 132 SHA_Bytes(&final, numberbuf, strlen(numberbuf)); 133 SHA_Final(&final, digest); 134 digestpos = 0; 135 } 136 steps[i].targetstart[j] ^= digest[digestpos++]; 137 } 138 139 /* 140 * Mask off the pad bits in the final byte after both steps. 141 */ 142 if (bits % 8) 143 bmp[bits / 8] &= 0xFF & (0xFF00 >> (bits % 8)); 144 } 145} 146 147/* err, yeah, these two pretty much rely on unsigned char being 8 bits. 148 * Platforms where this is not the case probably have bigger problems 149 * than just making these two work, though... */ 150char *bin2hex(const unsigned char *in, int inlen) 151{ 152 char *ret = snewn(inlen*2 + 1, char), *p = ret; 153 int i; 154 155 for (i = 0; i < inlen*2; i++) { 156 int v = in[i/2]; 157 if (i % 2 == 0) v >>= 4; 158 *p++ = "0123456789abcdef"[v & 0xF]; 159 } 160 *p = '\0'; 161 return ret; 162} 163 164unsigned char *hex2bin(const char *in, int outlen) 165{ 166 unsigned char *ret = snewn(outlen, unsigned char); 167 int i; 168 169 memset(ret, 0, outlen*sizeof(unsigned char)); 170 for (i = 0; i < outlen*2; i++) { 171 int c = in[i]; 172 int v; 173 174 assert(c != 0); 175 if (c >= '0' && c <= '9') 176 v = c - '0'; 177 else if (c >= 'a' && c <= 'f') 178 v = c - 'a' + 10; 179 else if (c >= 'A' && c <= 'F') 180 v = c - 'A' + 10; 181 else 182 v = 0; 183 184 ret[i / 2] |= v << (4 * (1 - (i % 2))); 185 } 186 return ret; 187} 188 189char *fgetline(FILE *fp) 190{ 191 char *ret = snewn(512, char); 192 int size = 512, len = 0; 193 while (fgets(ret + len, size - len, fp)) { 194 len += strlen(ret + len); 195 if (ret[len-1] == '\n') 196 break; /* got a newline, we're done */ 197 size = len + 512; 198 ret = sresize(ret, size, char); 199 } 200 if (len == 0) { /* first fgets returned NULL */ 201 sfree(ret); 202 return NULL; 203 } 204 ret[len] = '\0'; 205 return ret; 206} 207 208int getenv_bool(const char *name, int dflt) 209{ 210 char *env = getenv(name); 211 if (env == NULL) return dflt; 212 if (strchr("yYtT", env[0])) return true; 213 return false; 214} 215 216/* Utility functions for colour manipulation. */ 217 218static float colour_distance(const float a[3], const float b[3]) 219{ 220 return (float)sqrt((a[0]-b[0]) * (a[0]-b[0]) + 221 (a[1]-b[1]) * (a[1]-b[1]) + 222 (a[2]-b[2]) * (a[2]-b[2])); 223} 224 225void colour_mix(const float src1[3], const float src2[3], float p, float dst[3]) 226{ 227 int i; 228 for (i = 0; i < 3; i++) 229 dst[i] = src1[i] * (1.0F - p) + src2[i] * p; 230} 231 232void game_mkhighlight_specific(frontend *fe, float *ret, 233 int background, int highlight, int lowlight) 234{ 235 static const float black[3] = { 0.0F, 0.0F, 0.0F }; 236 static const float white[3] = { 1.0F, 1.0F, 1.0F }; 237 float db, dw; 238 int i; 239 /* 240 * New geometric highlight-generation algorithm: Draw a line from 241 * the base colour to white. The point K distance along this line 242 * from the base colour is the highlight colour. Similarly, draw 243 * a line from the base colour to black. The point on this line 244 * at a distance K from the base colour is the shadow. If either 245 * of these colours is imaginary (for reasonable K at most one 246 * will be), _extrapolate_ the base colour along the same line 247 * until it's a distance K from white (or black) and start again 248 * with that as the base colour. 249 * 250 * This preserves the hue of the base colour, ensures that of the 251 * three the base colour is the most saturated, and only ever 252 * flattens the highlight and shadow to pure white or pure black. 253 * 254 * K must be at most sqrt(3)/2, or mid grey would be too close to 255 * both white and black. Here K is set to sqrt(3)/6 so that this 256 * code produces the same results as the former code in the common 257 * case where the background is grey and the highlight saturates 258 * to white. 259 */ 260 const float k = sqrt(3)/6.0F; 261 if (lowlight >= 0) { 262 db = colour_distance(&ret[background*3], black); 263 if (db < k) { 264 for (i = 0; i < 3; i++) ret[lowlight*3+i] = black[i]; 265 if (db == 0.0F) 266 colour_mix(black, white, k/sqrt(3), &ret[background*3]); 267 else 268 colour_mix(black, &ret[background*3], k/db, &ret[background*3]); 269 } else { 270 colour_mix(&ret[background*3], black, k/db, &ret[lowlight*3]); 271 } 272 } 273 if (highlight >= 0) { 274 dw = colour_distance(&ret[background*3], white); 275 if (dw < k) { 276 for (i = 0; i < 3; i++) ret[highlight*3+i] = white[i]; 277 if (dw == 0.0F) 278 colour_mix(white, black, k/sqrt(3), &ret[background*3]); 279 else 280 colour_mix(white, &ret[background*3], k/dw, &ret[background*3]); 281 /* Background has changed; recalculate lowlight. */ 282 if (lowlight >= 0) 283 colour_mix(&ret[background*3], black, k/db, &ret[lowlight*3]); 284 } else { 285 colour_mix(&ret[background*3], white, k/dw, &ret[highlight*3]); 286 } 287 } 288} 289 290void game_mkhighlight(frontend *fe, float *ret, 291 int background, int highlight, int lowlight) 292{ 293 frontend_default_colour(fe, &ret[background * 3]); 294 game_mkhighlight_specific(fe, ret, background, highlight, lowlight); 295} 296 297void swap_regions(void *av, void *bv, size_t size) 298{ 299 char tmpbuf[512]; 300 char *a = av, *b = bv; 301 302 while (size > 0) { 303 int thislen = min(size, sizeof(tmpbuf)); 304 memcpy(tmpbuf, a, thislen); 305 memcpy(a, b, thislen); 306 memcpy(b, tmpbuf, thislen); 307 a += thislen; 308 b += thislen; 309 size -= thislen; 310 } 311} 312 313void shuffle(void *array, int nelts, int eltsize, random_state *rs) 314{ 315 char *carray = (char *)array; 316 int i; 317 318 for (i = nelts; i-- > 1 ;) { 319 int j = random_upto(rs, i+1); 320 if (j != i) 321 swap_regions(carray + eltsize * i, carray + eltsize * j, eltsize); 322 } 323} 324 325void draw_rect_outline(drawing *dr, int x, int y, int w, int h, int colour) 326{ 327 int x0 = x, x1 = x+w-1, y0 = y, y1 = y+h-1; 328 int coords[8]; 329 330 coords[0] = x0; 331 coords[1] = y0; 332 coords[2] = x0; 333 coords[3] = y1; 334 coords[4] = x1; 335 coords[5] = y1; 336 coords[6] = x1; 337 coords[7] = y0; 338 339 draw_polygon(dr, coords, 4, -1, colour); 340} 341 342void draw_rect_corners(drawing *dr, int cx, int cy, int r, int col) 343{ 344 draw_line(dr, cx - r, cy - r, cx - r, cy - r/2, col); 345 draw_line(dr, cx - r, cy - r, cx - r/2, cy - r, col); 346 draw_line(dr, cx - r, cy + r, cx - r, cy + r/2, col); 347 draw_line(dr, cx - r, cy + r, cx - r/2, cy + r, col); 348 draw_line(dr, cx + r, cy - r, cx + r, cy - r/2, col); 349 draw_line(dr, cx + r, cy - r, cx + r/2, cy - r, col); 350 draw_line(dr, cx + r, cy + r, cx + r, cy + r/2, col); 351 draw_line(dr, cx + r, cy + r, cx + r/2, cy + r, col); 352} 353 354int compare_integers(const void *av, const void *bv) { 355 const int *a = (const int *)av; 356 const int *b = (const int *)bv; 357 if (*a < *b) 358 return -1; 359 else if (*a > *b) 360 return +1; 361 else 362 return 0; 363} 364 365char *move_cursor(int button, int *x, int *y, int maxw, int maxh, bool wrap, 366 bool *visible) 367{ 368 int dx = 0, dy = 0, ox = *x, oy = *y; 369 switch (button) { 370 case CURSOR_UP: dy = -1; break; 371 case CURSOR_DOWN: dy = 1; break; 372 case CURSOR_RIGHT: dx = 1; break; 373 case CURSOR_LEFT: dx = -1; break; 374 default: return MOVE_UNUSED; 375 } 376 if (wrap) { 377 *x = (*x + dx + maxw) % maxw; 378 *y = (*y + dy + maxh) % maxh; 379 } else { 380 *x = min(max(*x+dx, 0), maxw - 1); 381 *y = min(max(*y+dy, 0), maxh - 1); 382 } 383 if (visible != NULL && !*visible) { 384 *visible = true; 385 return MOVE_UI_UPDATE; 386 } 387 if (*x != ox || *y != oy) 388 return MOVE_UI_UPDATE; 389 return MOVE_NO_EFFECT; 390} 391 392/* Used in netslide.c and sixteen.c for cursor movement around edge. */ 393 394int c2pos(int w, int h, int cx, int cy) 395{ 396 if (cy == -1) 397 return cx; /* top row, 0 .. w-1 (->) */ 398 else if (cx == w) 399 return w + cy; /* R col, w .. w+h -1 (v) */ 400 else if (cy == h) 401 return w + h + (w-cx-1); /* bottom row, w+h .. w+h+w-1 (<-) */ 402 else if (cx == -1) 403 return w + h + w + (h-cy-1); /* L col, w+h+w .. w+h+w+h-1 (^) */ 404 405 assert(!"invalid cursor pos!"); 406 return -1; /* not reached */ 407} 408 409int c2diff(int w, int h, int cx, int cy, int button) 410{ 411 int diff = 0; 412 413 assert(IS_CURSOR_MOVE(button)); 414 415 /* Obvious moves around edge. */ 416 if (cy == -1) 417 diff = (button == CURSOR_RIGHT) ? +1 : (button == CURSOR_LEFT) ? -1 : diff; 418 if (cy == h) 419 diff = (button == CURSOR_RIGHT) ? -1 : (button == CURSOR_LEFT) ? +1 : diff; 420 if (cx == -1) 421 diff = (button == CURSOR_UP) ? +1 : (button == CURSOR_DOWN) ? -1 : diff; 422 if (cx == w) 423 diff = (button == CURSOR_UP) ? -1 : (button == CURSOR_DOWN) ? +1 : diff; 424 425 if (button == CURSOR_LEFT && cx == w && (cy == 0 || cy == h-1)) 426 diff = (cy == 0) ? -1 : +1; 427 if (button == CURSOR_RIGHT && cx == -1 && (cy == 0 || cy == h-1)) 428 diff = (cy == 0) ? +1 : -1; 429 if (button == CURSOR_DOWN && cy == -1 && (cx == 0 || cx == w-1)) 430 diff = (cx == 0) ? -1 : +1; 431 if (button == CURSOR_UP && cy == h && (cx == 0 || cx == w-1)) 432 diff = (cx == 0) ? +1 : -1; 433 434 debug(("cx,cy = %d,%d; w%d h%d, diff = %d", cx, cy, w, h, diff)); 435 return diff; 436} 437 438void pos2c(int w, int h, int pos, int *cx, int *cy) 439{ 440 int max = w+h+w+h; 441 442 pos = (pos + max) % max; 443 444 if (pos < w) { 445 *cx = pos; *cy = -1; return; 446 } 447 pos -= w; 448 if (pos < h) { 449 *cx = w; *cy = pos; return; 450 } 451 pos -= h; 452 if (pos < w) { 453 *cx = w-pos-1; *cy = h; return; 454 } 455 pos -= w; 456 if (pos < h) { 457 *cx = -1; *cy = h-pos-1; return; 458 } 459 assert(!"invalid pos, huh?"); /* limited by % above! */ 460} 461 462void draw_text_outline(drawing *dr, int x, int y, int fonttype, 463 int fontsize, int align, 464 int text_colour, int outline_colour, const char *text) 465{ 466 if (outline_colour > -1) { 467 draw_text(dr, x-1, y, fonttype, fontsize, align, outline_colour, text); 468 draw_text(dr, x+1, y, fonttype, fontsize, align, outline_colour, text); 469 draw_text(dr, x, y-1, fonttype, fontsize, align, outline_colour, text); 470 draw_text(dr, x, y+1, fonttype, fontsize, align, outline_colour, text); 471 } 472 draw_text(dr, x, y, fonttype, fontsize, align, text_colour, text); 473 474} 475 476/* kludge for sprintf() in Rockbox not supporting "%-8.8s" */ 477void copy_left_justified(char *buf, size_t sz, const char *str) 478{ 479 size_t len = strlen(str); 480 assert(sz > 0); 481 memset(buf, ' ', sz - 1); 482 assert(len <= sz - 1); 483 memcpy(buf, str, len); 484 buf[sz - 1] = 0; 485} 486 487/* Returns a dynamically allocated label for a generic button. 488 * Game-specific buttons should go into the `label' field of key_label 489 * instead. */ 490char *button2label(int button) 491{ 492 /* check if it's a keyboard button */ 493 if(('A' <= button && button <= 'Z') || 494 ('a' <= button && button <= 'z') || 495 ('0' <= button && button <= '9') ) 496 { 497 char str[2]; 498 str[0] = button; 499 str[1] = '\0'; 500 return dupstr(str); 501 } 502 503 switch(button) 504 { 505 case CURSOR_UP: 506 return dupstr("Up"); 507 case CURSOR_DOWN: 508 return dupstr("Down"); 509 case CURSOR_LEFT: 510 return dupstr("Left"); 511 case CURSOR_RIGHT: 512 return dupstr("Right"); 513 case CURSOR_SELECT: 514 return dupstr("Select"); 515 case '\b': 516 return dupstr("Clear"); 517 default: 518 fatal("unknown generic key"); 519 } 520 521 /* should never get here */ 522 return NULL; 523} 524 525char *make_prefs_path(const char *dir, const char *sep, 526 const game *game, const char *suffix) 527{ 528 size_t dirlen = strlen(dir); 529 size_t seplen = strlen(sep); 530 size_t gamelen = strlen(game->name); 531 size_t suffixlen = strlen(suffix); 532 char *path, *p; 533 const char *q; 534 535 if (!dir || !sep || !game || !suffix) 536 return NULL; 537 538 path = snewn(dirlen + seplen + gamelen + suffixlen + 1, char); 539 p = path; 540 541 memcpy(p, dir, dirlen); 542 p += dirlen; 543 544 memcpy(p, sep, seplen); 545 p += seplen; 546 547 for (q = game->name; *q; q++) 548 if (*q != ' ') 549 *p++ = tolower((unsigned char)*q); 550 551 memcpy(p, suffix, suffixlen); 552 p += suffixlen; 553 554 *p = '\0'; 555 return path; 556} 557 558/* 559 * Calculate the nearest integer to n*sqrt(k), via a bitwise algorithm 560 * that avoids floating point. 561 * 562 * (It would probably be OK in practice to use floating point, but I 563 * felt like overengineering it for fun. With FP, there's at least a 564 * theoretical risk of rounding the wrong way, due to the three 565 * successive roundings involved - rounding sqrt(k), rounding its 566 * product with n, and then rounding to the nearest integer. This 567 * approach avoids that: it's exact.) 568 */ 569int n_times_root_k(int n_signed, int k) 570{ 571 unsigned x, r, m; 572 int sign = n_signed < 0 ? -1 : +1; 573 unsigned n = n_signed * sign; 574 unsigned bitpos; 575 576 /* 577 * Method: 578 * 579 * We transform m gradually from zero into n, by multiplying it by 580 * 2 in each step and optionally adding 1, so that it's always 581 * floor(n/2^something). 582 * 583 * At the start of each step, x is the largest integer less than 584 * or equal to m*sqrt(k). We transform m to 2m+bit, and therefore 585 * we must transform x to 2x+something to match. The 'something' 586 * we add to 2x is at most floor(sqrt(k))+2. (Worst case is if m 587 * sqrt(k) was equal to x + 1-eps for some tiny eps, and then the 588 * incoming bit of m is 1, so that (2m+1)sqrt(k) = 589 * 2x+2+sqrt(k)-2eps.) 590 * 591 * To compute this, we also track the residual value r such that 592 * x^2+r = km^2. 593 * 594 * The algorithm below is very similar to the usual approach for 595 * taking the square root of an integer in binary. The wrinkle is 596 * that we have an integer multiplier, i.e. we're computing 597 * n*sqrt(k) rather than just sqrt(k). Of course in principle we 598 * could just take sqrt(n^2k), but we'd need an integer twice the 599 * width to hold n^2. Pulling out n and treating it specially 600 * makes overflow less likely. 601 */ 602 603 x = r = m = 0; 604 605 for (bitpos = UINT_MAX & ~(UINT_MAX >> 1); bitpos; bitpos >>= 1) { 606 unsigned a, b = (n & bitpos) ? 1 : 0; 607 608 /* 609 * Check invariants. We expect that x^2 + r = km^2 (i.e. our 610 * residual term is correct), and also that r < 2x+1 (because 611 * if not, then we could replace x with x+1 and still get a 612 * value that made r non-negative, i.e. x would not be the 613 * _largest_ integer less than m sqrt(k)). 614 */ 615 assert(x*x + r == k*m*m); 616 assert(r < 2*x+1); 617 618 /* 619 * We're going to replace m with 2m+b, and x with 2x+a for 620 * some a we haven't decided on yet. 621 * 622 * The new value of the residual will therefore be 623 * 624 * k (2m+b)^2 - (2x+a)^2 625 * = (4km^2 + 4kmb + kb^2) - (4x^2 + 4xa + a^2) 626 * = 4 (km^2 - x^2) + 4kmb + kb^2 - 4xa - a^2 627 * = 4r + 4kmb + kb^2 - 4xa - a^2 (because r = km^2 - x^2) 628 * = 4r + (4m + 1)kb - 4xa - a^2 (b is 0 or 1, so b = b^2) 629 */ 630 for (a = 0;; a++) { 631 /* If we made this routine handle square roots of numbers 632 * significantly bigger than 3 or 5 then it would be 633 * sensible to make this a binary search. Here, it hardly 634 * seems important. */ 635 unsigned pos = 4*r + k*b*(4*m + 1); 636 unsigned neg = 4*a*x + a*a; 637 if (pos < neg) 638 break; /* this value of a is too big */ 639 } 640 641 /* The above loop will have terminated with a one too big. So 642 * now decrementing a will give us the right value to add. */ 643 a--; 644 645 r = 4*r + b*k*(4*m + 1) - (4*a*x + a*a); 646 m = 2*m+b; 647 x = 2*x+a; 648 } 649 650 /* 651 * Finally, round to the nearest integer. At present, x is the 652 * largest integer that is _at most_ m sqrt(k). But we want the 653 * _nearest_ integer, whether that's rounded up or down. So check 654 * whether (x + 1/2) is still less than m sqrt(k), i.e. whether 655 * (x + 1/2)^2 < km^2; if it is, then we increment x. 656 * 657 * We have km^2 - (x + 1/2)^2 = km^2 - x^2 - x - 1/4 658 * = r - x - 1/4 659 * 660 * and since r and x are integers, this is greater than 0 if and 661 * only if r > x. 662 * 663 * (There's no need to worry about tie-breaking exact halfway 664 * rounding cases. sqrt(k) is irrational, so none such exist.) 665 */ 666 if (r > x) 667 x++; 668 669 /* 670 * Put the sign back on, and convert back from unsigned to int. 671 */ 672 if (sign == +1) { 673 return x; 674 } else { 675 /* Be a little careful to avoid compilers deciding I've just 676 * perpetrated signed-integer overflow. This should optimise 677 * down to no actual code. */ 678 return INT_MIN + (int)(-x - (unsigned)INT_MIN); 679 } 680} 681 682/* vim: set shiftwidth=4 tabstop=8: */