]> git.saurik.com Git - wxWidgets.git/blame - src/mac/xpm/parse.c
OS/2 fixes
[wxWidgets.git] / src / mac / xpm / parse.c
CommitLineData
0240e8b1
SC
1/*
2 * Copyright (C) 1989-95 GROUPE BULL
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Except as contained in this notice, the name of GROUPE BULL shall not be
22 * used in advertising or otherwise to promote the sale, use or other dealings
23 * in this Software without prior written authorization from GROUPE BULL.
24 */
25
26/*****************************************************************************\
27* parse.c: *
28* *
29* XPM library *
30* Parse an XPM file or array and store the found informations *
31* in the given XpmImage structure. *
32* *
33* Developed by Arnaud Le Hors *
34\*****************************************************************************/
35
36/*
37 * The code related to FOR_MSW has been added by
38 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
39 */
40
2f1ae414 41#include "XpmI.h"
0240e8b1
SC
42#include <ctype.h>
43
44LFUNC(ParsePixels, int, (xpmData *data, unsigned int width,
45 unsigned int height, unsigned int ncolors,
46 unsigned int cpp, XpmColor *colorTable,
47 xpmHashTable *hashtable, unsigned int **pixels));
48
49char *xpmColorKeys[] = {
50 "s", /* key #1: symbol */
51 "m", /* key #2: mono visual */
52 "g4", /* key #3: 4 grays visual */
53 "g", /* key #4: gray visual */
54 "c", /* key #5: color visual */
55};
56
57int
58xpmParseValues(data, width, height, ncolors, cpp,
59 x_hotspot, y_hotspot, hotspot, extensions)
60 xpmData *data;
61 unsigned int *width, *height, *ncolors, *cpp;
62 unsigned int *x_hotspot, *y_hotspot, *hotspot;
63 unsigned int *extensions;
64{
65 unsigned int l;
66 char buf[BUFSIZ];
67
68 if (!data->format) { /* XPM 2 or 3 */
69
70 /*
71 * read values: width, height, ncolors, chars_per_pixel
72 */
73 if (!(xpmNextUI(data, width) && xpmNextUI(data, height)
74 && xpmNextUI(data, ncolors) && xpmNextUI(data, cpp)))
75 return (XpmFileInvalid);
76
77 /*
78 * read optional information (hotspot and/or XPMEXT) if any
79 */
80 l = xpmNextWord(data, buf, BUFSIZ);
81 if (l) {
82 *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
83 if (*extensions)
84 *hotspot = (xpmNextUI(data, x_hotspot)
85 && xpmNextUI(data, y_hotspot));
86 else {
87 *hotspot = (xpmatoui(buf, l, x_hotspot)
88 && xpmNextUI(data, y_hotspot));
89 l = xpmNextWord(data, buf, BUFSIZ);
90 *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
91 }
92 }
93 } else {
94
95 /*
96 * XPM 1 file read values: width, height, ncolors, chars_per_pixel
97 */
98 int i;
99 char *ptr;
100 Bool got_one, saw_width = False, saw_height = False;
101 Bool saw_ncolors = False, saw_chars_per_pixel = False;
102
103 for (i = 0; i < 4; i++) {
104 l = xpmNextWord(data, buf, BUFSIZ);
105 if (l != 7 || strncmp("#define", buf, 7))
106 return (XpmFileInvalid);
107 l = xpmNextWord(data, buf, BUFSIZ);
108 if (!l)
109 return (XpmFileInvalid);
110 buf[l] = '\0';
111 ptr = buf;
112 got_one = False;
113 while (!got_one) {
114 #ifdef macintosh // we have a strange parameter problem here
115 ptr = strchr(ptr, '_'); // index
116 #else
117 ptr = index(ptr, '_');
118 #endif
119 if (!ptr)
120 return (XpmFileInvalid);
121 switch (l - (ptr - buf)) {
122 case 6:
123 if (saw_width || strncmp("_width", ptr, 6)
124 || !xpmNextUI(data, width))
125 return (XpmFileInvalid);
126 else
127 saw_width = True;
128 got_one = True;
129 break;
130 case 7:
131 if (saw_height || strncmp("_height", ptr, 7)
132 || !xpmNextUI(data, height))
133 return (XpmFileInvalid);
134 else
135 saw_height = True;
136 got_one = True;
137 break;
138 case 8:
139 if (saw_ncolors || strncmp("_ncolors", ptr, 8)
140 || !xpmNextUI(data, ncolors))
141 return (XpmFileInvalid);
142 else
143 saw_ncolors = True;
144 got_one = True;
145 break;
146 case 16:
147 if (saw_chars_per_pixel
148 || strncmp("_chars_per_pixel", ptr, 16)
149 || !xpmNextUI(data, cpp))
150 return (XpmFileInvalid);
151 else
152 saw_chars_per_pixel = True;
153 got_one = True;
154 break;
155 default:
156 ptr++;
157 }
158 }
159 /* skip the end of line */
160 xpmNextString(data);
161 }
162 if (!saw_width || !saw_height || !saw_ncolors || !saw_chars_per_pixel)
163 return (XpmFileInvalid);
164
165 *hotspot = 0;
166 *extensions = 0;
167 }
168 return (XpmSuccess);
169}
170
171int
172xpmParseColors(data, ncolors, cpp, colorTablePtr, hashtable)
173 xpmData *data;
174 unsigned int ncolors;
175 unsigned int cpp;
176 XpmColor **colorTablePtr;
177 xpmHashTable *hashtable;
178{
179 unsigned int key, l, a, b;
180 unsigned int curkey; /* current color key */
181 unsigned int lastwaskey; /* key read */
182 char buf[BUFSIZ];
183 char curbuf[BUFSIZ]; /* current buffer */
184 char **sptr, *s;
185 XpmColor *color;
186 XpmColor *colorTable;
187 char **defaults;
188 int ErrorStatus;
189
190 colorTable = (XpmColor *) XpmCalloc(ncolors, sizeof(XpmColor));
191 if (!colorTable)
192 return (XpmNoMemory);
193
194 if (!data->format) { /* XPM 2 or 3 */
195 for (a = 0, color = colorTable; a < ncolors; a++, color++) {
196 xpmNextString(data); /* skip the line */
197
198 /*
199 * read pixel value
200 */
201 color->string = (char *) XpmMalloc(cpp + 1);
202 if (!color->string) {
203 xpmFreeColorTable(colorTable, ncolors);
204 return (XpmNoMemory);
205 }
206 for (b = 0, s = color->string; b < cpp; b++, s++)
207 *s = xpmGetC(data);
208 *s = '\0';
209
210 /*
211 * store the string in the hashtable with its color index number
212 */
213 if (USE_HASHTABLE) {
214 ErrorStatus =
215 xpmHashIntern(hashtable, color->string, HashAtomData(a));
216 if (ErrorStatus != XpmSuccess) {
217 xpmFreeColorTable(colorTable, ncolors);
218 return (ErrorStatus);
219 }
220 }
221
222 /*
223 * read color keys and values
224 */
225 defaults = (char **) color;
226 curkey = 0;
227 lastwaskey = 0;
228 *curbuf = '\0'; /* init curbuf */
229 while ((l = xpmNextWord(data, buf, BUFSIZ))!=NULL ) {
230 if (!lastwaskey) {
231 for (key = 0, sptr = xpmColorKeys; key < NKEYS; key++,
232 sptr++)
233 if ((strlen(*sptr) == l) && (!strncmp(*sptr, buf, l)))
234 break;
235 }
236 if (!lastwaskey && key < NKEYS) { /* open new key */
237 if (curkey) { /* flush string */
238 s = (char *) XpmMalloc(strlen(curbuf) + 1);
239 if (!s) {
240 xpmFreeColorTable(colorTable, ncolors);
241 return (XpmNoMemory);
242 }
243 defaults[curkey] = s;
244 strcpy(s, curbuf);
245 }
246 curkey = key + 1; /* set new key */
247 *curbuf = '\0'; /* reset curbuf */
248 lastwaskey = 1;
249 } else {
250 if (!curkey) { /* key without value */
251 xpmFreeColorTable(colorTable, ncolors);
252 return (XpmFileInvalid);
253 }
254 if (!lastwaskey)
255 strcat(curbuf, " "); /* append space */
256 buf[l] = '\0';
257 strcat(curbuf, buf);/* append buf */
258 lastwaskey = 0;
259 }
260 }
261 if (!curkey) { /* key without value */
262 xpmFreeColorTable(colorTable, ncolors);
263 return (XpmFileInvalid);
264 }
265 s = defaults[curkey] = (char *) XpmMalloc(strlen(curbuf) + 1);
266 if (!s) {
267 xpmFreeColorTable(colorTable, ncolors);
268 return (XpmNoMemory);
269 }
270 strcpy(s, curbuf);
271 }
272 } else { /* XPM 1 */
273 /* get to the beginning of the first string */
274 data->Bos = '"';
275 data->Eos = '\0';
276 xpmNextString(data);
277 data->Eos = '"';
278 for (a = 0, color = colorTable; a < ncolors; a++, color++) {
279
280 /*
281 * read pixel value
282 */
283 color->string = (char *) XpmMalloc(cpp + 1);
284 if (!color->string) {
285 xpmFreeColorTable(colorTable, ncolors);
286 return (XpmNoMemory);
287 }
288 for (b = 0, s = color->string; b < cpp; b++, s++)
289 *s = xpmGetC(data);
290 *s = '\0';
291
292 /*
293 * store the string in the hashtable with its color index number
294 */
295 if (USE_HASHTABLE) {
296 ErrorStatus =
297 xpmHashIntern(hashtable, color->string, HashAtomData(a));
298 if (ErrorStatus != XpmSuccess) {
299 xpmFreeColorTable(colorTable, ncolors);
300 return (ErrorStatus);
301 }
302 }
303
304 /*
305 * read color values
306 */
307 xpmNextString(data); /* get to the next string */
308 *curbuf = '\0'; /* init curbuf */
309 while ((l = xpmNextWord(data, buf, BUFSIZ))!=NULL ) {
310 if (*curbuf != '\0')
311 strcat(curbuf, " ");/* append space */
312 buf[l] = '\0';
313 strcat(curbuf, buf); /* append buf */
314 }
315 s = (char *) XpmMalloc(strlen(curbuf) + 1);
316 if (!s) {
317 xpmFreeColorTable(colorTable, ncolors);
318 return (XpmNoMemory);
319 }
320 strcpy(s, curbuf);
321 color->c_color = s;
322 *curbuf = '\0'; /* reset curbuf */
323 if (a < ncolors - 1)
324 xpmNextString(data); /* get to the next string */
325 }
326 }
327 *colorTablePtr = colorTable;
328 return (XpmSuccess);
329}
330
331static int
332ParsePixels(data, width, height, ncolors, cpp, colorTable, hashtable, pixels)
333 xpmData *data;
334 unsigned int width;
335 unsigned int height;
336 unsigned int ncolors;
337 unsigned int cpp;
338 XpmColor *colorTable;
339 xpmHashTable *hashtable;
340 unsigned int **pixels;
341{
342 unsigned int *iptr, *iptr2;
343 unsigned int a, x, y;
344
345#ifndef FOR_MSW
346 iptr2 = (unsigned int *) XpmMalloc(sizeof(unsigned int) * width * height);
347#else
348
349 /*
350 * special treatment to trick DOS malloc(size_t) where size_t is 16 bit!!
351 * XpmMalloc is defined to longMalloc(long) and checks the 16 bit boundary
352 */
353 iptr2 = (unsigned int *)
354 XpmMalloc((long) sizeof(unsigned int) * (long) width * (long) height);
355#endif
356 if (!iptr2)
357 return (XpmNoMemory);
358
359 iptr = iptr2;
360
361 switch (cpp) {
362
363 case (1): /* Optimize for single character
364 * colors */
365 {
366 unsigned short colidx[256];
367
368 bzero((char *)colidx, 256 * sizeof(short));
369 for (a = 0; a < ncolors; a++)
370 colidx[(unsigned char)colorTable[a].string[0]] = a + 1;
371
372 for (y = 0; y < height; y++) {
373 xpmNextString(data);
374 for (x = 0; x < width; x++, iptr++) {
375 int c = xpmGetC(data);
376
377 if (c > 0 && c < 256 && colidx[c] != 0)
378 *iptr = colidx[c] - 1;
379 else {
380 XpmFree(iptr2);
381 return (XpmFileInvalid);
382 }
383 }
384 }
385 }
386 break;
387
388 case (2): /* Optimize for double character
389 * colors */
390 {
391
392/* free all allocated pointers at all exits */
393#define FREE_CIDX {int f; for (f = 0; f < 256; f++) \
394if (cidx[f]) XpmFree(cidx[f]);}
395
396 /* array of pointers malloced by need */
397 unsigned short *cidx[256];
398 int char1;
399
400 bzero((char *)cidx, 256 * sizeof(unsigned short *)); /* init */
401 for (a = 0; a < ncolors; a++) {
402 char1 = colorTable[a].string[0];
403 if (cidx[char1] == NULL) { /* get new memory */
404 cidx[char1] = (unsigned short *)
405 XpmCalloc(256, sizeof(unsigned short));
406 if (cidx[char1] == NULL) { /* new block failed */
407 FREE_CIDX;
408 XpmFree(iptr2);
409 return (XpmNoMemory);
410 }
411 }
412 cidx[char1][(unsigned char)colorTable[a].string[1]] = a + 1;
413 }
414
415 for (y = 0; y < height; y++) {
416 xpmNextString(data);
417 for (x = 0; x < width; x++, iptr++) {
418 int cc1 = xpmGetC(data);
419 if (cc1 > 0 && cc1 < 256) {
420 int cc2 = xpmGetC(data);
421 if (cc2 > 0 && cc2 < 256 &&
422 cidx[cc1] && cidx[cc1][cc2] != 0)
423 *iptr = cidx[cc1][cc2] - 1;
424 else {
425 FREE_CIDX;
426 XpmFree(iptr2);
427 return (XpmFileInvalid);
428 }
429 } else {
430 FREE_CIDX;
431 XpmFree(iptr2);
432 return (XpmFileInvalid);
433 }
434 }
435 }
436 FREE_CIDX;
437 }
438 break;
439
440 default: /* Non-optimized case of long color
441 * names */
442 {
443 char *s;
444 char buf[BUFSIZ];
445
446 buf[cpp] = '\0';
447 if (USE_HASHTABLE) {
448 xpmHashAtom *slot;
449
450 for (y = 0; y < height; y++) {
451 xpmNextString(data);
452 for (x = 0; x < width; x++, iptr++) {
453 for (a = 0, s = buf; a < cpp; a++, s++)
454 *s = xpmGetC(data);
455 slot = xpmHashSlot(hashtable, buf);
456 if (!*slot) { /* no color matches */
457 XpmFree(iptr2);
458 return (XpmFileInvalid);
459 }
460 *iptr = HashColorIndex(slot);
461 }
462 }
463 } else {
464 for (y = 0; y < height; y++) {
465 xpmNextString(data);
466 for (x = 0; x < width; x++, iptr++) {
467 for (a = 0, s = buf; a < cpp; a++, s++)
468 *s = xpmGetC(data);
469 for (a = 0; a < ncolors; a++)
470 if (!strcmp(colorTable[a].string, buf))
471 break;
472 if (a == ncolors) { /* no color matches */
473 XpmFree(iptr2);
474 return (XpmFileInvalid);
475 }
476 *iptr = a;
477 }
478 }
479 }
480 }
481 break;
482 }
483 *pixels = iptr2;
484 return (XpmSuccess);
485}
486
487int
488xpmParseExtensions(data, extensions, nextensions)
489 xpmData *data;
490 XpmExtension **extensions;
491 unsigned int *nextensions;
492{
493 XpmExtension *exts = NULL, *ext;
494 unsigned int num = 0;
495 unsigned int nlines, a, l, notstart, notend = 0;
496 int status;
497 char *string, *s, *s2, **sp;
498
499 xpmNextString(data);
500 exts = (XpmExtension *) XpmMalloc(sizeof(XpmExtension));
501 /* get the whole string */
502 status = xpmGetString(data, &string, &l);
503 if (status != XpmSuccess) {
504 XpmFree(exts);
505 return (status);
506 }
507 /* look for the key word XPMEXT, skip lines before this */
508 while ((notstart = strncmp("XPMEXT", string, 6))!=NULL
509 && (notend = strncmp("XPMENDEXT", string, 9))!=NULL ) {
510 XpmFree(string);
511 xpmNextString(data);
512 status = xpmGetString(data, &string, &l);
513 if (status != XpmSuccess) {
514 XpmFree(exts);
515 return (status);
516 }
517 }
518 if (!notstart)
519 notend = strncmp("XPMENDEXT", string, 9);
520 while (!notstart && notend) {
521 /* there starts an extension */
522 ext = (XpmExtension *)
523 XpmRealloc(exts, (num + 1) * sizeof(XpmExtension));
524 if (!ext) {
525 XpmFree(string);
526 XpmFreeExtensions(exts, num);
527 return (XpmNoMemory);
528 }
529 exts = ext;
530 ext += num;
531 /* skip whitespace and store its name */
532 s2 = s = string + 6;
533 while (isspace(*s2))
534 s2++;
535 a = s2 - s;
536 ext->name = (char *) XpmMalloc(l - a - 6);
537 if (!ext->name) {
538 XpmFree(string);
539 ext->lines = NULL;
540 ext->nlines = 0;
541 XpmFreeExtensions(exts, num + 1);
542 return (XpmNoMemory);
543 }
544 strncpy(ext->name, s + a, l - a - 6);
545 XpmFree(string);
546 /* now store the related lines */
547 xpmNextString(data);
548 status = xpmGetString(data, &string, &l);
549 if (status != XpmSuccess) {
550 ext->lines = NULL;
551 ext->nlines = 0;
552 XpmFreeExtensions(exts, num + 1);
553 return (status);
554 }
555 ext->lines = (char **) XpmMalloc(sizeof(char *));
556 nlines = 0;
557 while ((notstart = strncmp("XPMEXT", string, 6))!=NULL
558 && (notend = strncmp("XPMENDEXT", string, 9))!=NULL ) {
559 sp = (char **)
560 XpmRealloc(ext->lines, (nlines + 1) * sizeof(char *));
561 if (!sp) {
562 XpmFree(string);
563 ext->nlines = nlines;
564 XpmFreeExtensions(exts, num + 1);
565 return (XpmNoMemory);
566 }
567 ext->lines = sp;
568 ext->lines[nlines] = string;
569 nlines++;
570 xpmNextString(data);
571 status = xpmGetString(data, &string, &l);
572 if (status != XpmSuccess) {
573 ext->nlines = nlines;
574 XpmFreeExtensions(exts, num + 1);
575 return (status);
576 }
577 }
578 if (!nlines) {
579 XpmFree(ext->lines);
580 ext->lines = NULL;
581 }
582 ext->nlines = nlines;
583 num++;
584 }
585 if (!num) {
586 XpmFree(string);
587 XpmFree(exts);
588 exts = NULL;
589 } else if (!notend)
590 XpmFree(string);
591 *nextensions = num;
592 *extensions = exts;
593 return (XpmSuccess);
594}
595
596
597/* function call in case of error */
598#undef RETURN
599#define RETURN(status) \
600{ \
601 goto error; \
602}
603
604/*
605 * This function parses an Xpm file or data and store the found informations
606 * in an an XpmImage structure which is returned.
607 */
608int
609xpmParseData(data, image, info)
610 xpmData *data;
611 XpmImage *image;
612 XpmInfo *info;
613{
614 /* variables to return */
615 unsigned int width, height, ncolors, cpp;
616 unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
617 XpmColor *colorTable = NULL;
618 unsigned int *pixelindex = NULL;
619 char *hints_cmt = NULL;
620 char *colors_cmt = NULL;
621 char *pixels_cmt = NULL;
622
623 unsigned int cmts;
624 int ErrorStatus;
625 xpmHashTable hashtable;
626
627 cmts = info && (info->valuemask & XpmReturnComments);
628
629 /*
630 * parse the header
631 */
632 ErrorStatus = xpmParseHeader(data);
633 if (ErrorStatus != XpmSuccess)
634 return (ErrorStatus);
635
636 /*
637 * read values
638 */
639 ErrorStatus = xpmParseValues(data, &width, &height, &ncolors, &cpp,
640 &x_hotspot, &y_hotspot, &hotspot,
641 &extensions);
642 if (ErrorStatus != XpmSuccess)
643 return (ErrorStatus);
644
645 /*
646 * store the hints comment line
647 */
648 if (cmts)
649 xpmGetCmt(data, &hints_cmt);
650
651 /*
652 * init the hastable
653 */
654 if (USE_HASHTABLE) {
655 ErrorStatus = xpmHashTableInit(&hashtable);
656 if (ErrorStatus != XpmSuccess)
657 return (ErrorStatus);
658 }
659
660 /*
661 * read colors
662 */
663 ErrorStatus = xpmParseColors(data, ncolors, cpp, &colorTable, &hashtable);
664 if (ErrorStatus != XpmSuccess) {
665 if (USE_HASHTABLE)
666 xpmHashTableFree(&hashtable);
667 RETURN(ErrorStatus);
668 }
669
670 /*
671 * store the colors comment line
672 */
673 if (cmts)
674 xpmGetCmt(data, &colors_cmt);
675
676 /*
677 * read pixels and index them on color number
678 */
679 ErrorStatus = ParsePixels(data, width, height, ncolors, cpp, colorTable,
680 &hashtable, &pixelindex);
681
682 /*
683 * free the hastable
684 */
685 if (USE_HASHTABLE)
686 xpmHashTableFree(&hashtable);
687
688 if (ErrorStatus != XpmSuccess)
689 RETURN(ErrorStatus);
690
691 /*
692 * store the pixels comment line
693 */
694 if (cmts)
695 xpmGetCmt(data, &pixels_cmt);
696
697 /*
698 * parse extensions
699 */
700 if (info && (info->valuemask & XpmReturnExtensions))
701 if (extensions) {
702 ErrorStatus = xpmParseExtensions(data, &info->extensions,
703 &info->nextensions);
704 if (ErrorStatus != XpmSuccess)
705 RETURN(ErrorStatus);
706 } else {
707 info->extensions = NULL;
708 info->nextensions = 0;
709 }
710
711 /*
712 * store found informations in the XpmImage structure
713 */
714 image->width = width;
715 image->height = height;
716 image->cpp = cpp;
717 image->ncolors = ncolors;
718 image->colorTable = colorTable;
719 image->data = pixelindex;
720
721 if (info) {
722 if (cmts) {
723 info->hints_cmt = hints_cmt;
724 info->colors_cmt = colors_cmt;
725 info->pixels_cmt = pixels_cmt;
726 }
727 if (hotspot) {
728 info->x_hotspot = x_hotspot;
729 info->y_hotspot = y_hotspot;
730 info->valuemask |= XpmHotspot;
731 }
732 }
733 return (XpmSuccess);
734
735/* exit point in case of error, free only locally allocated variables */
736error:
737 if (colorTable)
738 xpmFreeColorTable(colorTable, ncolors);
739 if (pixelindex)
740 XpmFree(pixelindex);
741 if (hints_cmt)
742 XpmFree(hints_cmt);
743 if (colors_cmt)
744 XpmFree(colors_cmt);
745 if (pixels_cmt)
746 XpmFree(pixels_cmt);
747
748 return(ErrorStatus);
749}