Statistics
| Revision:

root / logic / trunk / src / mxml / mxml-node.c @ 49

History | View | Annotate | Download (17.9 KB)

1
/*
2
 * "$Id: mxml-node.c 363 2008-10-26 18:28:05Z mike $"
3
 *
4
 * Node support code for Mini-XML, a small XML-like file parsing library.
5
 *
6
 * Copyright 2003-2007 by Michael Sweet.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Library General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * Contents:
19
 *
20
 *   mxmlAdd()        - Add a node to a tree.
21
 *   mxmlDelete()     - Delete a node and all of its children.
22
 *   mxmlNewCDATA()   - Create a new CDATA node.
23
 *   mxmlNewCustom()  - Create a new custom data node.
24
 *   mxmlNewElement() - Create a new element node.
25
 *   mxmlNewInteger() - Create a new integer node.
26
 *   mxmlNewOpaque()  - Create a new opaque string.
27
 *   mxmlNewReal()    - Create a new real number node.
28
 *   mxmlNewText()    - Create a new text fragment node.
29
 *   mxmlNewTextf()   - Create a new formatted text fragment node.
30
 *   mxmlNewXML()     - Create a new XML document tree.
31
 *   mxmlRelease()    - Release a node.
32
 *   mxmlRemove()     - Remove a node from its parent.
33
 *   mxmlRetain()     - Retain a node.
34
 *   mxml_new()       - Create a new node.
35
 */
36

    
37
/*
38
 * Include necessary headers...
39
 */
40

    
41
#include "config.h"
42
#include "mxml.h"
43

    
44

    
45
/*
46
 * Local functions...
47
 */
48

    
49
static mxml_node_t        *mxml_new(mxml_node_t *parent, mxml_type_t type);
50

    
51

    
52
/*
53
 * 'mxmlAdd()' - Add a node to a tree.
54
 *
55
 * Adds the specified node to the parent. If the child argument is not
56
 * NULL, puts the new node before or after the specified child depending
57
 * on the value of the where argument. If the child argument is NULL,
58
 * puts the new node at the beginning of the child list (MXML_ADD_BEFORE)
59
 * or at the end of the child list (MXML_ADD_AFTER). The constant
60
 * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer.
61
 */
62

    
63
void
64
mxmlAdd(mxml_node_t *parent,                /* I - Parent node */
65
        int         where,                /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */
66
        mxml_node_t *child,                /* I - Child node for where or MXML_ADD_TO_PARENT */
67
        mxml_node_t *node)                /* I - Node to add */
68
{
69
#ifdef DEBUG
70
  fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent,
71
          where, child, node);
72
#endif /* DEBUG */
73

    
74
 /*
75
  * Range check input...
76
  */
77

    
78
  if (!parent || !node)
79
    return;
80

    
81
#if DEBUG > 1
82
  fprintf(stderr, "    BEFORE: node->parent=%p\n", node->parent);
83
  if (parent)
84
  {
85
    fprintf(stderr, "    BEFORE: parent->child=%p\n", parent->child);
86
    fprintf(stderr, "    BEFORE: parent->last_child=%p\n", parent->last_child);
87
    fprintf(stderr, "    BEFORE: parent->prev=%p\n", parent->prev);
88
    fprintf(stderr, "    BEFORE: parent->next=%p\n", parent->next);
89
  }
90
#endif /* DEBUG > 1 */
91

    
92
 /*
93
  * Remove the node from any existing parent...
94
  */
95

    
96
  if (node->parent)
97
    mxmlRemove(node);
98

    
99
 /*
100
  * Reset pointers...
101
  */
102

    
103
  node->parent = parent;
104

    
105
  switch (where)
106
  {
107
    case MXML_ADD_BEFORE :
108
        if (!child || child == parent->child || child->parent != parent)
109
        {
110
         /*
111
          * Insert as first node under parent...
112
          */
113

    
114
          node->next = parent->child;
115

    
116
          if (parent->child)
117
            parent->child->prev = node;
118
          else
119
            parent->last_child = node;
120

    
121
          parent->child = node;
122
        }
123
        else
124
        {
125
         /*
126
          * Insert node before this child...
127
          */
128

    
129
          node->next = child;
130
          node->prev = child->prev;
131

    
132
          if (child->prev)
133
            child->prev->next = node;
134
          else
135
            parent->child = node;
136

    
137
          child->prev = node;
138
        }
139
        break;
140

    
141
    case MXML_ADD_AFTER :
142
        if (!child || child == parent->last_child || child->parent != parent)
143
        {
144
         /*
145
          * Insert as last node under parent...
146
          */
147

    
148
          node->parent = parent;
149
          node->prev   = parent->last_child;
150

    
151
          if (parent->last_child)
152
            parent->last_child->next = node;
153
          else
154
            parent->child = node;
155

    
156
          parent->last_child = node;
157
        }
158
        else
159
        {
160
         /*
161
          * Insert node after this child...
162
          */
163

    
164
          node->prev = child;
165
          node->next = child->next;
166

    
167
          if (child->next)
168
            child->next->prev = node;
169
          else
170
            parent->last_child = node;
171

    
172
          child->next = node;
173
        }
174
        break;
175
  }
176

    
177
#if DEBUG > 1
178
  fprintf(stderr, "    AFTER: node->parent=%p\n", node->parent);
179
  if (parent)
180
  {
181
    fprintf(stderr, "    AFTER: parent->child=%p\n", parent->child);
182
    fprintf(stderr, "    AFTER: parent->last_child=%p\n", parent->last_child);
183
    fprintf(stderr, "    AFTER: parent->prev=%p\n", parent->prev);
184
    fprintf(stderr, "    AFTER: parent->next=%p\n", parent->next);
185
  }
186
#endif /* DEBUG > 1 */
187
}
188

    
189

    
190
/*
191
 * 'mxmlDelete()' - Delete a node and all of its children.
192
 *
193
 * If the specified node has a parent, this function first removes the
194
 * node from its parent using the mxmlRemove() function.
195
 */
196

    
197
void
198
mxmlDelete(mxml_node_t *node)                /* I - Node to delete */
199
{
200
  int        i;                                /* Looping var */
201

    
202

    
203
#ifdef DEBUG
204
  fprintf(stderr, "mxmlDelete(node=%p)\n", node);
205
#endif /* DEBUG */
206

    
207
 /*
208
  * Range check input...
209
  */
210

    
211
  if (!node)
212
    return;
213

    
214
 /*
215
  * Remove the node from its parent, if any...
216
  */
217

    
218
  mxmlRemove(node);
219

    
220
 /*
221
  * Delete children...
222
  */
223

    
224
  while (node->child)
225
    mxmlDelete(node->child);
226

    
227
 /*
228
  * Now delete any node data...
229
  */
230

    
231
  switch (node->type)
232
  {
233
    case MXML_ELEMENT :
234
        if (node->value.element.name)
235
          free(node->value.element.name);
236

    
237
        if (node->value.element.num_attrs)
238
        {
239
          for (i = 0; i < node->value.element.num_attrs; i ++)
240
          {
241
            if (node->value.element.attrs[i].name)
242
              free(node->value.element.attrs[i].name);
243
            if (node->value.element.attrs[i].value)
244
              free(node->value.element.attrs[i].value);
245
          }
246

    
247
          free(node->value.element.attrs);
248
        }
249
        break;
250
    case MXML_INTEGER :
251
       /* Nothing to do */
252
        break;
253
    case MXML_OPAQUE :
254
        if (node->value.opaque)
255
          free(node->value.opaque);
256
        break;
257
    case MXML_REAL :
258
       /* Nothing to do */
259
        break;
260
    case MXML_TEXT :
261
        if (node->value.text.string)
262
          free(node->value.text.string);
263
        break;
264
    case MXML_CUSTOM :
265
        if (node->value.custom.data &&
266
            node->value.custom.destroy)
267
          (*(node->value.custom.destroy))(node->value.custom.data);
268
        break;
269
    default :
270
        break;
271
  }
272

    
273
 /*
274
  * Free this node...
275
  */
276

    
277
  free(node);
278
}
279

    
280

    
281
/*
282
 * 'mxmlNewCDATA()' - Create a new CDATA node.
283
 *
284
 * The new CDATA node is added to the end of the specified parent's child
285
 * list. The constant MXML_NO_PARENT can be used to specify that the new
286
 * CDATA node has no parent. The data string must be nul-terminated and
287
 * is copied into the new node. CDATA nodes use the MXML_ELEMENT type.
288
 *
289
 * @since Mini-XML 2.3@
290
 */
291

    
292
mxml_node_t *                                /* O - New node */
293
mxmlNewCDATA(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
294
             const char  *data)                /* I - Data string */
295
{
296
  mxml_node_t        *node;                        /* New node */
297

    
298

    
299
#ifdef DEBUG
300
  fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n",
301
          parent, data ? data : "(null)");
302
#endif /* DEBUG */
303

    
304
 /*
305
  * Range check input...
306
  */
307

    
308
  if (!data)
309
    return (NULL);
310

    
311
 /*
312
  * Create the node and set the name value...
313
  */
314

    
315
  if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
316
    node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data);
317

    
318
  return (node);
319
}
320

    
321

    
322
/*
323
 * 'mxmlNewCustom()' - Create a new custom data node.
324
 *
325
 * The new custom node is added to the end of the specified parent's child
326
 * list. The constant MXML_NO_PARENT can be used to specify that the new
327
 * element node has no parent. NULL can be passed when the data in the
328
 * node is not dynamically allocated or is separately managed.
329
 *
330
 * @since Mini-XML 2.1@
331
 */
332

    
333
mxml_node_t *                                /* O - New node */
334
mxmlNewCustom(
335
    mxml_node_t              *parent,        /* I - Parent node or MXML_NO_PARENT */
336
    void                     *data,        /* I - Pointer to data */
337
    mxml_custom_destroy_cb_t destroy)        /* I - Function to destroy data */
338
{
339
  mxml_node_t        *node;                        /* New node */
340

    
341

    
342
#ifdef DEBUG
343
  fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent,
344
          data, destroy);
345
#endif /* DEBUG */
346

    
347
 /*
348
  * Create the node and set the value...
349
  */
350

    
351
  if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL)
352
  {
353
    node->value.custom.data    = data;
354
    node->value.custom.destroy = destroy;
355
  }
356

    
357
  return (node);
358
}
359

    
360

    
361
/*
362
 * 'mxmlNewElement()' - Create a new element node.
363
 *
364
 * The new element node is added to the end of the specified parent's child
365
 * list. The constant MXML_NO_PARENT can be used to specify that the new
366
 * element node has no parent.
367
 */
368

    
369
mxml_node_t *                                /* O - New node */
370
mxmlNewElement(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
371
               const char  *name)        /* I - Name of element */
372
{
373
  mxml_node_t        *node;                        /* New node */
374

    
375

    
376
#ifdef DEBUG
377
  fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent,
378
          name ? name : "(null)");
379
#endif /* DEBUG */
380

    
381
 /*
382
  * Range check input...
383
  */
384

    
385
  if (!name)
386
    return (NULL);
387

    
388
 /*
389
  * Create the node and set the element name...
390
  */
391

    
392
  if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL)
393
    node->value.element.name = strdup(name);
394

    
395
  return (node);
396
}
397

    
398

    
399
/*
400
 * 'mxmlNewInteger()' - Create a new integer node.
401
 *
402
 * The new integer node is added to the end of the specified parent's child
403
 * list. The constant MXML_NO_PARENT can be used to specify that the new
404
 * integer node has no parent.
405
 */
406

    
407
mxml_node_t *                                /* O - New node */
408
mxmlNewInteger(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
409
               int         integer)        /* I - Integer value */
410
{
411
  mxml_node_t        *node;                        /* New node */
412

    
413

    
414
#ifdef DEBUG
415
  fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer);
416
#endif /* DEBUG */
417

    
418
 /*
419
  * Create the node and set the element name...
420
  */
421

    
422
  if ((node = mxml_new(parent, MXML_INTEGER)) != NULL)
423
    node->value.integer = integer;
424

    
425
  return (node);
426
}
427

    
428

    
429
/*
430
 * 'mxmlNewOpaque()' - Create a new opaque string.
431
 *
432
 * The new opaque node is added to the end of the specified parent's child
433
 * list. The constant MXML_NO_PARENT can be used to specify that the new
434
 * opaque node has no parent. The opaque string must be nul-terminated and
435
 * is copied into the new node.
436
 */
437

    
438
mxml_node_t *                                /* O - New node */
439
mxmlNewOpaque(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
440
              const char  *opaque)        /* I - Opaque string */
441
{
442
  mxml_node_t        *node;                        /* New node */
443

    
444

    
445
#ifdef DEBUG
446
  fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent,
447
          opaque ? opaque : "(null)");
448
#endif /* DEBUG */
449

    
450
 /*
451
  * Range check input...
452
  */
453

    
454
  if (!opaque)
455
    return (NULL);
456

    
457
 /*
458
  * Create the node and set the element name...
459
  */
460

    
461
  if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL)
462
    node->value.opaque = strdup(opaque);
463

    
464
  return (node);
465
}
466

    
467

    
468
/*
469
 * 'mxmlNewReal()' - Create a new real number node.
470
 *
471
 * The new real number node is added to the end of the specified parent's
472
 * child list. The constant MXML_NO_PARENT can be used to specify that
473
 * the new real number node has no parent.
474
 */
475

    
476
mxml_node_t *                                /* O - New node */
477
mxmlNewReal(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
478
            double      real)                /* I - Real number value */
479
{
480
  mxml_node_t        *node;                        /* New node */
481

    
482

    
483
#ifdef DEBUG
484
  fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real);
485
#endif /* DEBUG */
486

    
487
 /*
488
  * Create the node and set the element name...
489
  */
490

    
491
  if ((node = mxml_new(parent, MXML_REAL)) != NULL)
492
    node->value.real = real;
493

    
494
  return (node);
495
}
496

    
497

    
498
/*
499
 * 'mxmlNewText()' - Create a new text fragment node.
500
 *
501
 * The new text node is added to the end of the specified parent's child
502
 * list. The constant MXML_NO_PARENT can be used to specify that the new
503
 * text node has no parent. The whitespace parameter is used to specify
504
 * whether leading whitespace is present before the node. The text
505
 * string must be nul-terminated and is copied into the new node.  
506
 */
507

    
508
mxml_node_t *                                /* O - New node */
509
mxmlNewText(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
510
            int         whitespace,        /* I - 1 = leading whitespace, 0 = no whitespace */
511
            const char  *string)        /* I - String */
512
{
513
  mxml_node_t        *node;                        /* New node */
514

    
515

    
516
#ifdef DEBUG
517
  fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n",
518
          parent, whitespace, string ? string : "(null)");
519
#endif /* DEBUG */
520

    
521
 /*
522
  * Range check input...
523
  */
524

    
525
  if (!string)
526
    return (NULL);
527

    
528
 /*
529
  * Create the node and set the text value...
530
  */
531

    
532
  if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
533
  {
534
    node->value.text.whitespace = whitespace;
535
    node->value.text.string     = strdup(string);
536
  }
537

    
538
  return (node);
539
}
540

    
541

    
542
/*
543
 * 'mxmlNewTextf()' - Create a new formatted text fragment node.
544
 *
545
 * The new text node is added to the end of the specified parent's child
546
 * list. The constant MXML_NO_PARENT can be used to specify that the new
547
 * text node has no parent. The whitespace parameter is used to specify
548
 * whether leading whitespace is present before the node. The format
549
 * string must be nul-terminated and is formatted into the new node.  
550
 */
551

    
552
mxml_node_t *                                /* O - New node */
553
mxmlNewTextf(mxml_node_t *parent,        /* I - Parent node or MXML_NO_PARENT */
554
             int         whitespace,        /* I - 1 = leading whitespace, 0 = no whitespace */
555
             const char  *format,        /* I - Printf-style frmat string */
556
             ...)                        /* I - Additional args as needed */
557
{
558
  mxml_node_t        *node;                        /* New node */
559
  va_list        ap;                        /* Pointer to arguments */
560

    
561

    
562
#ifdef DEBUG
563
  fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n",
564
          parent, whitespace, format ? format : "(null)");
565
#endif /* DEBUG */
566

    
567
 /*
568
  * Range check input...
569
  */
570

    
571
  if (!format)
572
    return (NULL);
573

    
574
 /*
575
  * Create the node and set the text value...
576
  */
577

    
578
  if ((node = mxml_new(parent, MXML_TEXT)) != NULL)
579
  {
580
    va_start(ap, format);
581

    
582
    node->value.text.whitespace = whitespace;
583
    node->value.text.string     = _mxml_vstrdupf(format, ap);
584

    
585
    va_end(ap);
586
  }
587

    
588
  return (node);
589
}
590

    
591

    
592
/*
593
 * 'mxmlRemove()' - Remove a node from its parent.
594
 *
595
 * Does not free memory used by the node - use mxmlDelete() for that.
596
 * This function does nothing if the node has no parent.
597
 */
598

    
599
void
600
mxmlRemove(mxml_node_t *node)                /* I - Node to remove */
601
{
602
#ifdef DEBUG
603
  fprintf(stderr, "mxmlRemove(node=%p)\n", node);
604
#endif /* DEBUG */
605

    
606
 /*
607
  * Range check input...
608
  */
609

    
610
  if (!node || !node->parent)
611
    return;
612

    
613
 /*
614
  * Remove from parent...
615
  */
616

    
617
#if DEBUG > 1
618
  fprintf(stderr, "    BEFORE: node->parent=%p\n", node->parent);
619
  if (node->parent)
620
  {
621
    fprintf(stderr, "    BEFORE: node->parent->child=%p\n", node->parent->child);
622
    fprintf(stderr, "    BEFORE: node->parent->last_child=%p\n", node->parent->last_child);
623
  }
624
  fprintf(stderr, "    BEFORE: node->child=%p\n", node->child);
625
  fprintf(stderr, "    BEFORE: node->last_child=%p\n", node->last_child);
626
  fprintf(stderr, "    BEFORE: node->prev=%p\n", node->prev);
627
  fprintf(stderr, "    BEFORE: node->next=%p\n", node->next);
628
#endif /* DEBUG > 1 */
629

    
630
  if (node->prev)
631
    node->prev->next = node->next;
632
  else
633
    node->parent->child = node->next;
634

    
635
  if (node->next)
636
    node->next->prev = node->prev;
637
  else
638
    node->parent->last_child = node->prev;
639

    
640
  node->parent = NULL;
641
  node->prev   = NULL;
642
  node->next   = NULL;
643

    
644
#if DEBUG > 1
645
  fprintf(stderr, "    AFTER: node->parent=%p\n", node->parent);
646
  if (node->parent)
647
  {
648
    fprintf(stderr, "    AFTER: node->parent->child=%p\n", node->parent->child);
649
    fprintf(stderr, "    AFTER: node->parent->last_child=%p\n", node->parent->last_child);
650
  }
651
  fprintf(stderr, "    AFTER: node->child=%p\n", node->child);
652
  fprintf(stderr, "    AFTER: node->last_child=%p\n", node->last_child);
653
  fprintf(stderr, "    AFTER: node->prev=%p\n", node->prev);
654
  fprintf(stderr, "    AFTER: node->next=%p\n", node->next);
655
#endif /* DEBUG > 1 */
656
}
657

    
658

    
659
/*
660
 * 'mxmlNewXML()' - Create a new XML document tree.
661
 *
662
 * The "version" argument specifies the version number to put in the
663
 * ?xml element node. If NULL, version 1.0 is assumed.
664
 *
665
 * @since Mini-XML 2.3@
666
 */
667

    
668
mxml_node_t *                                /* O - New ?xml node */
669
mxmlNewXML(const char *version)                /* I - Version number to use */
670
{
671
  char        element[1024];                        /* Element text */
672

    
673

    
674
  snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?",
675
           version ? version : "1.0");
676

    
677
  return (mxmlNewElement(NULL, element));
678
}
679

    
680

    
681
/*
682
 * 'mxmlRelease()' - Release a node.
683
 *
684
 * When the reference count reaches zero, the node (and any children)
685
 * is deleted via mxmlDelete().
686
 *
687
 * @since Mini-XML 2.3@
688
 */
689

    
690
int                                        /* O - New reference count */
691
mxmlRelease(mxml_node_t *node)                /* I - Node */
692
{
693
  if (node)
694
  {
695
    if ((-- node->ref_count) <= 0)
696
    {
697
      mxmlDelete(node);
698
      return (0);
699
    }
700
    else
701
      return (node->ref_count);
702
  }
703
  else
704
    return (-1);
705
}
706

    
707

    
708
/*
709
 * 'mxmlRetain()' - Retain a node.
710
 *
711
 * @since Mini-XML 2.3@
712
 */
713

    
714
int                                        /* O - New reference count */
715
mxmlRetain(mxml_node_t *node)                /* I - Node */
716
{
717
  if (node)
718
    return (++ node->ref_count);
719
  else
720
    return (-1);
721
}
722

    
723

    
724
/*
725
 * 'mxml_new()' - Create a new node.
726
 */
727

    
728
static mxml_node_t *                        /* O - New node */
729
mxml_new(mxml_node_t *parent,                /* I - Parent node */
730
         mxml_type_t type)                /* I - Node type */
731
{
732
  mxml_node_t        *node;                        /* New node */
733

    
734

    
735
#if DEBUG > 1
736
  fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type);
737
#endif /* DEBUG > 1 */
738

    
739
 /*
740
  * Allocate memory for the node...
741
  */
742

    
743
  if ((node = calloc(1, sizeof(mxml_node_t))) == NULL)
744
  {
745
#if DEBUG > 1
746
    fputs("    returning NULL\n", stderr);
747
#endif /* DEBUG > 1 */
748

    
749
    return (NULL);
750
  }
751

    
752
#if DEBUG > 1
753
  fprintf(stderr, "    returning %p\n", node);
754
#endif /* DEBUG > 1 */
755

    
756
 /*
757
  * Set the node type...
758
  */
759

    
760
  node->type      = type;
761
  node->ref_count = 1;
762

    
763
 /*
764
  * Add to the parent if present...
765
  */
766

    
767
  if (parent)
768
    mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
769

    
770
 /*
771
  * Return the new node...
772
  */
773

    
774
  return (node);
775
}
776

    
777

    
778
/*
779
 * End of "$Id: mxml-node.c 363 2008-10-26 18:28:05Z mike $".
780
 */