summaryrefslogtreecommitdiff
path: root/liolib.c
diff options
context:
space:
mode:
Diffstat (limited to 'liolib.c')
-rw-r--r--liolib.c196
1 files changed, 109 insertions, 87 deletions
diff --git a/liolib.c b/liolib.c
index dc432b40..b73bd359 100644
--- a/liolib.c
+++ b/liolib.c
@@ -1,5 +1,5 @@
1/* 1/*
2** $Id: liolib.c,v 1.70 2000/08/14 19:10:14 roberto Exp roberto $ 2** $Id: liolib.c,v 1.71 2000/08/22 17:47:17 roberto Exp roberto $
3** Standard I/O (and system) library 3** Standard I/O (and system) library
4** See Copyright Notice in lua.h 4** See Copyright Notice in lua.h
5*/ 5*/
@@ -61,13 +61,16 @@ typedef struct IOCtrl {
61static const char *const filenames[] = {"_INPUT", "_OUTPUT"}; 61static const char *const filenames[] = {"_INPUT", "_OUTPUT"};
62 62
63 63
64static void pushresult (lua_State *L, int i) { 64static int pushresult (lua_State *L, int i) {
65 if (i) 65 if (i) {
66 lua_pushuserdata(L, NULL); 66 lua_pushuserdata(L, NULL);
67 return 1;
68 }
67 else { 69 else {
68 lua_pushnil(L); 70 lua_pushnil(L);
69 lua_pushstring(L, strerror(errno)); 71 lua_pushstring(L, strerror(errno));
70 lua_pushnumber(L, errno); 72 lua_pushnumber(L, errno);
73 return 3;;
71 } 74 }
72} 75}
73 76
@@ -79,8 +82,8 @@ static void pushresult (lua_State *L, int i) {
79*/ 82*/
80 83
81 84
82static FILE *gethandle (lua_State *L, IOCtrl *ctrl, lua_Object f) { 85static FILE *gethandle (lua_State *L, IOCtrl *ctrl, int f) {
83 void *p = lua_getuserdata(L, f); 86 void *p = lua_touserdata(L, f);
84 if (p != NULL) { /* is `f' a userdata ? */ 87 if (p != NULL) { /* is `f' a userdata ? */
85 int ftag = lua_tag(L, f); 88 int ftag = lua_tag(L, f);
86 if (ftag == ctrl->iotag) /* does it have the correct tag? */ 89 if (ftag == ctrl->iotag) /* does it have the correct tag? */
@@ -94,7 +97,7 @@ static FILE *gethandle (lua_State *L, IOCtrl *ctrl, lua_Object f) {
94 97
95 98
96static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) { 99static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) {
97 FILE *f = gethandle(L, ctrl, lua_getparam(L, arg)); 100 FILE *f = gethandle(L, ctrl, arg);
98 luaL_arg_check(L, f, arg, "invalid file handle"); 101 luaL_arg_check(L, f, arg, "invalid file handle");
99 return f; 102 return f;
100} 103}
@@ -102,9 +105,11 @@ static FILE *getnonullfile (lua_State *L, IOCtrl *ctrl, int arg) {
102 105
103static FILE *getfilebyref (lua_State *L, IOCtrl *ctrl, int inout) { 106static FILE *getfilebyref (lua_State *L, IOCtrl *ctrl, int inout) {
104 FILE *f; 107 FILE *f;
105 lua_pushglobals(L); 108 lua_getglobals(L);
106 lua_pushref(L, ctrl->ref[inout]); 109 lua_getref(L, ctrl->ref[inout]);
107 f = gethandle(L, ctrl, lua_rawget(L)); 110 lua_rawget(L);
111 f = gethandle(L, ctrl, -1);
112 lua_settop(L, -1); /* remove global */
108 if (f == NULL) 113 if (f == NULL)
109 luaL_verror(L, "global variable `%.10s' is not a file handle", 114 luaL_verror(L, "global variable `%.10s' is not a file handle",
110 filenames[inout]); 115 filenames[inout]);
@@ -122,12 +127,13 @@ static void setfilebyname (lua_State *L, IOCtrl *ctrl, FILE *f,
122#define setfile(L,ctrl,f,inout) (setfilebyname(L,ctrl,f,filenames[inout])) 127#define setfile(L,ctrl,f,inout) (setfilebyname(L,ctrl,f,filenames[inout]))
123 128
124 129
125static void setreturn (lua_State *L, IOCtrl *ctrl, FILE *f, int inout) { 130static int setreturn (lua_State *L, IOCtrl *ctrl, FILE *f, int inout) {
126 if (f == NULL) 131 if (f == NULL)
127 pushresult(L, 0); 132 return pushresult(L, 0);
128 else { 133 else {
129 setfile(L, ctrl, f, inout); 134 setfile(L, ctrl, f, inout);
130 lua_pushusertag(L, f, ctrl->iotag); 135 lua_pushusertag(L, f, ctrl->iotag);
136 return 1;
131 } 137 }
132} 138}
133 139
@@ -143,15 +149,15 @@ static int closefile (lua_State *L, IOCtrl *ctrl, FILE *f) {
143} 149}
144 150
145 151
146static void io_close (lua_State *L) { 152static int io_close (lua_State *L) {
147 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 153 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
148 pushresult(L, closefile(L, ctrl, getnonullfile(L, ctrl, 2))); 154 return pushresult(L, closefile(L, ctrl, getnonullfile(L, ctrl, 2)));
149} 155}
150 156
151 157
152static void file_collect (lua_State *L) { 158static int file_collect (lua_State *L) {
153 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 159 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
154 if (ctrl == (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 2))) { 160 if (ctrl == (IOCtrl *)lua_touserdata(L, 2)) {
155 /* collectig `ctrl' itself */ 161 /* collectig `ctrl' itself */
156 lua_unref(L, ctrl->ref[INFILE]); 162 lua_unref(L, ctrl->ref[INFILE]);
157 lua_unref(L, ctrl->ref[OUTFILE]); 163 lua_unref(L, ctrl->ref[OUTFILE]);
@@ -162,50 +168,54 @@ static void file_collect (lua_State *L) {
162 if (f != stdin && f != stdout && f != stderr) 168 if (f != stdin && f != stdout && f != stderr)
163 CLOSEFILE(L, f); 169 CLOSEFILE(L, f);
164 } 170 }
171 return 0;
165} 172}
166 173
167 174
168static void io_open (lua_State *L) { 175static int io_open (lua_State *L) {
169 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 176 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
170 FILE *f = fopen(luaL_check_string(L, 2), luaL_check_string(L, 3)); 177 FILE *f = fopen(luaL_check_string(L, 2), luaL_check_string(L, 3));
171 if (f) lua_pushusertag(L, f, ctrl->iotag); 178 if (f) {
172 else pushresult(L, 0); 179 lua_pushusertag(L, f, ctrl->iotag);
180 return 1;
181 }
182 else
183 return pushresult(L, 0);
173} 184}
174 185
175 186
176 187
177static void io_fromto (lua_State *L, int inout, const char *mode) { 188static int io_fromto (lua_State *L, int inout, const char *mode) {
178 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 189 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
179 lua_Object f = lua_getparam(L, 2);
180 FILE *current; 190 FILE *current;
181 if (f == LUA_NOOBJECT) { 191 if (lua_isnull(L, 2)) {
182 closefile(L, ctrl, getfilebyref(L, ctrl, inout)); 192 closefile(L, ctrl, getfilebyref(L, ctrl, inout));
183 current = (inout == 0) ? stdin : stdout; 193 current = (inout == 0) ? stdin : stdout;
184 } 194 }
185 else if (lua_tag(L, f) == ctrl->iotag) /* deprecated option */ 195 else if (lua_tag(L, 2) == ctrl->iotag) /* deprecated option */
186 current = (FILE *)lua_getuserdata(L, f); 196 current = (FILE *)lua_touserdata(L, 2);
187 else { 197 else {
188 const char *s = luaL_check_string(L, 2); 198 const char *s = luaL_check_string(L, 2);
189 current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode); 199 current = (*s == '|') ? popen(s+1, mode) : fopen(s, mode);
190 } 200 }
191 setreturn(L, ctrl, current, inout); 201 return setreturn(L, ctrl, current, inout);
192} 202}
193 203
194 204
195static void io_readfrom (lua_State *L) { 205static int io_readfrom (lua_State *L) {
196 io_fromto(L, INFILE, "r"); 206 return io_fromto(L, INFILE, "r");
197} 207}
198 208
199 209
200static void io_writeto (lua_State *L) { 210static int io_writeto (lua_State *L) {
201 io_fromto(L, OUTFILE, "w"); 211 return io_fromto(L, OUTFILE, "w");
202} 212}
203 213
204 214
205static void io_appendto (lua_State *L) { 215static int io_appendto (lua_State *L) {
206 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 216 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
207 FILE *current = fopen(luaL_check_string(L, 2), "a"); 217 FILE *current = fopen(luaL_check_string(L, 2), "a");
208 setreturn(L, ctrl, current, OUTFILE); 218 return setreturn(L, ctrl, current, OUTFILE);
209} 219}
210 220
211 221
@@ -342,28 +352,29 @@ static int read_chars (lua_State *L, FILE *f, size_t n) {
342} 352}
343 353
344 354
345static void io_read (lua_State *L) { 355static int io_read (lua_State *L) {
346 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 356 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
347 int arg = 2; 357 int lastarg = lua_gettop(L);
348 lua_Object op; 358 int firstarg = 2;
349 FILE *f = gethandle(L, ctrl, lua_getparam(L, arg)); 359 FILE *f = gethandle(L, ctrl, firstarg);
350 if (f) arg++; 360 int n = 0;
361 if (f) firstarg++;
351 else f = getfilebyref(L, ctrl, INFILE); /* get _INPUT */ 362 else f = getfilebyref(L, ctrl, INFILE); /* get _INPUT */
352 op = lua_getparam(L, arg);
353 do { /* repeat for each part */ 363 do { /* repeat for each part */
354 size_t l; 364 size_t l;
355 int success; 365 int success;
356 luaL_resetbuffer(L); 366 luaL_resetbuffer(L);
357 if (lua_isnumber(L, op)) 367 if (lua_isnumber(L, firstarg+n))
358 success = read_chars(L, f, (size_t)lua_getnumber(L, op)); 368 success = read_chars(L, f, (size_t)lua_tonumber(L, firstarg+n));
359 else { 369 else {
360 const char *p = luaL_opt_string(L, arg, "*l"); 370 const char *p = luaL_opt_string(L, firstarg+n, "*l");
361 if (p[0] != '*') 371 if (p[0] != '*')
362 success = read_pattern(L, f, p); /* deprecated! */ 372 success = read_pattern(L, f, p); /* deprecated! */
363 else { 373 else {
364 switch (p[1]) { 374 switch (p[1]) {
365 case 'n': /* number */ 375 case 'n': /* number */
366 if (!read_number(L, f)) return; /* read fails */ 376 if (!read_number(L, f)) return n; /* read fails */
377 n++;
367 continue; /* number is already pushed; avoid the "pushstring" */ 378 continue; /* number is already pushed; avoid the "pushstring" */
368 case 'l': /* line */ 379 case 'l': /* line */
369 success = read_line(L, f); 380 success = read_line(L, f);
@@ -377,66 +388,69 @@ static void io_read (lua_State *L) {
377 success = 0; /* must read something to succeed */ 388 success = 0; /* must read something to succeed */
378 break; 389 break;
379 default: 390 default:
380 luaL_argerror(L, arg, "invalid format"); 391 luaL_argerror(L, firstarg+n, "invalid format");
381 success = 0; /* to avoid warnings */ 392 success = 0; /* to avoid warnings */
382 } 393 }
383 } 394 }
384 } 395 }
385 l = luaL_getsize(L); 396 l = luaL_getsize(L);
386 if (!success && l==0) return; /* read fails */ 397 if (!success && l==0) return n; /* read fails */
387 lua_pushlstring(L, luaL_buffer(L), l); 398 lua_pushlstring(L, luaL_buffer(L), l);
388 } while ((op = lua_getparam(L, ++arg)) != LUA_NOOBJECT); 399 n++;
400 } while (firstarg+n <= lastarg);
401 return n;
389} 402}
390 403
391/* }====================================================== */ 404/* }====================================================== */
392 405
393 406
394static void io_write (lua_State *L) { 407static int io_write (lua_State *L) {
395 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 408 int lastarg = lua_gettop(L);
409 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
396 int arg = 2; 410 int arg = 2;
397 int status = 1; 411 int status = 1;
398 lua_Object o; 412 FILE *f = gethandle(L, ctrl, arg);
399 FILE *f = gethandle(L, ctrl, lua_getparam(L, arg));
400 if (f) arg++; 413 if (f) arg++;
401 else f = getfilebyref(L, ctrl, OUTFILE); /* get _OUTPUT */ 414 else f = getfilebyref(L, ctrl, OUTFILE); /* get _OUTPUT */
402 while ((o = lua_getparam(L, arg)) != LUA_NOOBJECT) { 415 for (; arg <= lastarg; arg++) {
403 if (lua_type(L, o)[2] == 'm') { /* nuMber? */ /* LUA_NUMBER */ 416 if (lua_type(L, arg)[2] == 'm') { /* nuMber? */ /* LUA_NUMBER */
404 /* optimization: could be done exactly as for strings */ 417 /* optimization: could be done exactly as for strings */
405 status = status && fprintf(f, "%.16g", lua_getnumber(L, o)) > 0; 418 status = status && fprintf(f, "%.16g", lua_tonumber(L, arg)) > 0;
406 } 419 }
407 else { 420 else {
408 size_t l; 421 size_t l;
409 const char *s = luaL_check_lstr(L, arg, &l); 422 const char *s = luaL_check_lstr(L, arg, &l);
410 status = status && (fwrite(s, sizeof(char), l, f) == l); 423 status = status && (fwrite(s, sizeof(char), l, f) == l);
411 } 424 }
412 arg++;
413 } 425 }
414 pushresult(L, status); 426 pushresult(L, status);
427 return 1;
415} 428}
416 429
417 430
418static void io_seek (lua_State *L) { 431static int io_seek (lua_State *L) {
419 static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; 432 static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
420 static const char *const modenames[] = {"set", "cur", "end", NULL}; 433 static const char *const modenames[] = {"set", "cur", "end", NULL};
421 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 434 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
422 FILE *f = getnonullfile(L, ctrl, 2); 435 FILE *f = getnonullfile(L, ctrl, 2);
423 int op = luaL_findstring(luaL_opt_string(L, 3, "cur"), modenames); 436 int op = luaL_findstring(luaL_opt_string(L, 3, "cur"), modenames);
424 long offset = luaL_opt_long(L, 4, 0); 437 long offset = luaL_opt_long(L, 4, 0);
425 luaL_arg_check(L, op != -1, 3, "invalid mode"); 438 luaL_arg_check(L, op != -1, 3, "invalid mode");
426 op = fseek(f, offset, mode[op]); 439 op = fseek(f, offset, mode[op]);
427 if (op) 440 if (op)
428 pushresult(L, 0); /* error */ 441 return pushresult(L, 0); /* error */
429 else 442 else {
430 lua_pushnumber(L, ftell(f)); 443 lua_pushnumber(L, ftell(f));
444 return 1;
445 }
431} 446}
432 447
433 448
434static void io_flush (lua_State *L) { 449static int io_flush (lua_State *L) {
435 IOCtrl *ctrl = (IOCtrl *)lua_getuserdata(L, lua_getparam(L, 1)); 450 IOCtrl *ctrl = (IOCtrl *)lua_touserdata(L, 1);
436 lua_Object of = lua_getparam(L, 2); 451 FILE *f = gethandle(L, ctrl, 2);
437 FILE *f = gethandle(L, ctrl, of); 452 luaL_arg_check(L, f || lua_isnull(L, 2), 2, "invalid file handle");
438 luaL_arg_check(L, f || of == LUA_NOOBJECT, 2, "invalid file handle"); 453 return pushresult(L, fflush(f) == 0);
439 pushresult(L, fflush(f) == 0);
440} 454}
441 455
442/* }====================================================== */ 456/* }====================================================== */
@@ -448,39 +462,43 @@ static void io_flush (lua_State *L) {
448** ======================================================= 462** =======================================================
449*/ 463*/
450 464
451static void io_execute (lua_State *L) { 465static int io_execute (lua_State *L) {
452 lua_pushnumber(L, system(luaL_check_string(L, 1))); 466 lua_pushnumber(L, system(luaL_check_string(L, 1)));
467 return 1;
453} 468}
454 469
455 470
456static void io_remove (lua_State *L) { 471static int io_remove (lua_State *L) {
457 pushresult(L, remove(luaL_check_string(L, 1)) == 0); 472 return pushresult(L, remove(luaL_check_string(L, 1)) == 0);
458} 473}
459 474
460 475
461static void io_rename (lua_State *L) { 476static int io_rename (lua_State *L) {
462 pushresult(L, rename(luaL_check_string(L, 1), 477 return pushresult(L, rename(luaL_check_string(L, 1),
463 luaL_check_string(L, 2)) == 0); 478 luaL_check_string(L, 2)) == 0);
464} 479}
465 480
466 481
467static void io_tmpname (lua_State *L) { 482static int io_tmpname (lua_State *L) {
468 lua_pushstring(L, tmpnam(NULL)); 483 lua_pushstring(L, tmpnam(NULL));
484 return 1;
469} 485}
470 486
471 487
472 488
473static void io_getenv (lua_State *L) { 489static int io_getenv (lua_State *L) {
474 lua_pushstring(L, getenv(luaL_check_string(L, 1))); /* if NULL push nil */ 490 lua_pushstring(L, getenv(luaL_check_string(L, 1))); /* if NULL push nil */
491 return 1;
475} 492}
476 493
477 494
478static void io_clock (lua_State *L) { 495static int io_clock (lua_State *L) {
479 lua_pushnumber(L, ((double)clock())/CLOCKS_PER_SEC); 496 lua_pushnumber(L, ((double)clock())/CLOCKS_PER_SEC);
497 return 1;
480} 498}
481 499
482 500
483static void io_date (lua_State *L) { 501static int io_date (lua_State *L) {
484 char b[256]; 502 char b[256];
485 const char *s = luaL_opt_string(L, 1, "%c"); 503 const char *s = luaL_opt_string(L, 1, "%c");
486 struct tm *stm; 504 struct tm *stm;
@@ -490,10 +508,11 @@ static void io_date (lua_State *L) {
490 lua_pushstring(L, b); 508 lua_pushstring(L, b);
491 else 509 else
492 lua_error(L, "invalid `date' format"); 510 lua_error(L, "invalid `date' format");
511 return 1;
493} 512}
494 513
495 514
496static void setloc (lua_State *L) { 515static int setloc (lua_State *L) {
497 static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, 516 static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
498 LC_NUMERIC, LC_TIME}; 517 LC_NUMERIC, LC_TIME};
499 static const char *const catnames[] = {"all", "collate", "ctype", "monetary", 518 static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
@@ -501,25 +520,28 @@ static void setloc (lua_State *L) {
501 int op = luaL_findstring(luaL_opt_string(L, 2, "all"), catnames); 520 int op = luaL_findstring(luaL_opt_string(L, 2, "all"), catnames);
502 luaL_arg_check(L, op != -1, 2, "invalid option"); 521 luaL_arg_check(L, op != -1, 2, "invalid option");
503 lua_pushstring(L, setlocale(cat[op], luaL_check_string(L, 1))); 522 lua_pushstring(L, setlocale(cat[op], luaL_check_string(L, 1)));
523 return 1;
504} 524}
505 525
506 526
507static void io_exit (lua_State *L) { 527static int io_exit (lua_State *L) {
508 exit(luaL_opt_int(L, 1, EXIT_SUCCESS)); 528 exit(luaL_opt_int(L, 1, EXIT_SUCCESS));
529 return 0; /* to avoid warnings */
509} 530}
510 531
511/* }====================================================== */ 532/* }====================================================== */
512 533
513 534
514 535
515static void io_debug (lua_State *L) { 536static int io_debug (lua_State *L) {
516 for (;;) { 537 for (;;) {
517 char buffer[250]; 538 char buffer[250];
518 fprintf(stderr, "lua_debug> "); 539 fprintf(stderr, "lua_debug> ");
519 if (fgets(buffer, sizeof(buffer), stdin) == 0 || 540 if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
520 strcmp(buffer, "cont\n") == 0) 541 strcmp(buffer, "cont\n") == 0)
521 return; 542 return 0;
522 lua_dostring(L, buffer); 543 lua_dostring(L, buffer);
544 lua_settop(L, 0); /* remove eventual returns */
523 } 545 }
524} 546}
525 547
@@ -529,12 +551,11 @@ static void io_debug (lua_State *L) {
529#define MAXMESSAGE (MESSAGESIZE*10) 551#define MAXMESSAGE (MESSAGESIZE*10)
530 552
531 553
532static void errorfb (lua_State *L) { 554static int errorfb (lua_State *L) {
533 char buff[MAXMESSAGE]; 555 char buff[MAXMESSAGE];
534 int level = 1; /* skip level 0 (it's this function) */ 556 int level = 1; /* skip level 0 (it's this function) */
535 lua_Debug ar; 557 lua_Debug ar;
536 lua_Object alertfunc; 558 sprintf(buff, "error: %.200s\n", lua_tostring(L, 1));
537 sprintf(buff, "error: %.200s\n", lua_getstring(L, lua_getparam(L, 1)));
538 while (lua_getstack(L, level++, &ar)) { 559 while (lua_getstack(L, level++, &ar)) {
539 char buffchunk[60]; 560 char buffchunk[60];
540 lua_getinfo(L, "Snl", &ar); 561 lua_getinfo(L, "Snl", &ar);
@@ -572,13 +593,14 @@ static void errorfb (lua_State *L) {
572 sprintf(buff+strlen(buff), " [%.70s]", buffchunk); 593 sprintf(buff+strlen(buff), " [%.70s]", buffchunk);
573 strcat(buff, "\n"); 594 strcat(buff, "\n");
574 } 595 }
575 lua_pushglobals(L); 596 lua_getglobals(L);
576 lua_pushstring(L, LUA_ALERT); 597 lua_pushstring(L, LUA_ALERT);
577 alertfunc = lua_rawget(L); 598 lua_rawget(L);
578 if (lua_isfunction(L, alertfunc)) { /* avoid loop if _ALERT is not defined */ 599 if (lua_isfunction(L, -1)) { /* avoid loop if _ALERT is not defined */
579 lua_pushstring(L, buff); 600 lua_pushstring(L, buff);
580 lua_callfunction(L, alertfunc); 601 lua_call(L, 1, 0);
581 } 602 }
603 return 0;
582} 604}
583 605
584 606