]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/snmp-supplies.c
Merge changes from CUPS 1.4svn-r8639.
[thirdparty/cups.git] / backend / snmp-supplies.c
CommitLineData
568fa3fa
MS
1/*
2 * "$Id$"
3 *
4 * SNMP supplies functions for the Common UNIX Printing System (CUPS).
5 *
d1c13e16 6 * Copyright 2008-2009 by Apple Inc.
568fa3fa
MS
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Apple Inc. and are protected by Federal copyright
10 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged, see the license at "http://www.cups.org/".
13 *
14 * This file is subject to the Apple OS-Developed Software exception.
15 *
16 * Contents:
17 *
18 * backendSNMPSupplies() - Get the current supplies for a device.
19 * backend_init_supplies() - Initialize the supplies list.
20 * backend_walk_cb() - Interpret the supply value responses...
21 */
22
23/*
24 * Include necessary headers.
25 */
26
27#include "backend-private.h"
28#include <cups/array.h>
29
30
31/*
32 * Local constants...
33 */
34
35#define CUPS_MAX_SUPPLIES 32 /* Maximum number of supplies for a printer */
36
37
38/*
39 * Local structures...
40 */
41
745129be 42typedef struct /**** Printer supply data ****/
568fa3fa
MS
43{
44 char name[CUPS_SNMP_MAX_STRING], /* Name of supply */
45 color[8]; /* Color: "#RRGGBB" or "none" */
46 int colorant, /* Colorant index */
47 type, /* Supply type */
48 max_capacity, /* Maximum capacity */
49 level; /* Current level value */
50} backend_supplies_t;
51
745129be
MS
52typedef struct /**** Printer state table ****/
53{
54 int bit; /* State bit */
55 const char *keyword; /* IPP printer-state-reasons keyword */
56} backend_state_t;
57
568fa3fa
MS
58
59/*
60 * Local globals...
61 */
62
63static http_addr_t current_addr; /* Current address */
745129be
MS
64static int current_state = -1;
65 /* Current device state bits */
568fa3fa
MS
66static int num_supplies = 0;
67 /* Number of supplies found */
68static backend_supplies_t supplies[CUPS_MAX_SUPPLIES];
69 /* Supply information */
70
71static const int hrDeviceDescr[] =
72 { CUPS_OID_hrDeviceDescr, 1, -1 };
73 /* Device description OID */
74static const int hrPrinterStatus[] =
75 { CUPS_OID_hrPrinterStatus, 1, -1 };
76 /* Current state OID */
77static const int hrPrinterDetectedErrorState[] =
78 { CUPS_OID_hrPrinterDetectedErrorState, 1, -1 };
79 /* Current printer state bits OID */
80static const int prtMarkerColorantValue[] =
81 { CUPS_OID_prtMarkerColorantValue, -1 },
82 /* Colorant OID */
83 prtMarkerColorantValueOffset =
84 (sizeof(prtMarkerColorantValue) /
85 sizeof(prtMarkerColorantValue[0]));
86 /* Offset to colorant index */
87static const int prtMarkerLifeCount[] =
88 { CUPS_OID_prtMarkerLifeCount, 1, 1, -1 };
89 /* Page counter OID */
90static const int prtMarkerSuppliesEntry[] =
91 { CUPS_OID_prtMarkerSuppliesEntry, -1 };
92 /* Supplies OID */
93static const int prtMarkerSuppliesColorantIndex[] =
94 { CUPS_OID_prtMarkerSuppliesColorantIndex, -1 },
95 /* Colorant index OID */
96 prtMarkerSuppliesColorantIndexOffset =
97 (sizeof(prtMarkerSuppliesColorantIndex) /
98 sizeof(prtMarkerSuppliesColorantIndex[0]));
99 /* Offset to supply index */
100static const int prtMarkerSuppliesDescription[] =
101 { CUPS_OID_prtMarkerSuppliesDescription, -1 },
102 /* Description OID */
103 prtMarkerSuppliesDescriptionOffset =
104 (sizeof(prtMarkerSuppliesDescription) /
105 sizeof(prtMarkerSuppliesDescription[0]));
106 /* Offset to supply index */
107static const int prtMarkerSuppliesLevel[] =
108 { CUPS_OID_prtMarkerSuppliesLevel, -1 },
109 /* Level OID */
110 prtMarkerSuppliesLevelOffset =
111 (sizeof(prtMarkerSuppliesLevel) /
112 sizeof(prtMarkerSuppliesLevel[0]));
113 /* Offset to supply index */
114static const int prtMarkerSuppliesMaxCapacity[] =
115 { CUPS_OID_prtMarkerSuppliesMaxCapacity, -1 },
116 /* Max capacity OID */
117 prtMarkerSuppliesMaxCapacityOffset =
118 (sizeof(prtMarkerSuppliesMaxCapacity) /
119 sizeof(prtMarkerSuppliesMaxCapacity[0]));
120 /* Offset to supply index */
121static const int prtMarkerSuppliesType[] =
122 { CUPS_OID_prtMarkerSuppliesType, -1 },
123 /* Type OID */
124 prtMarkerSuppliesTypeOffset =
125 (sizeof(prtMarkerSuppliesType) /
126 sizeof(prtMarkerSuppliesType[0]));
127 /* Offset to supply index */
128
745129be
MS
129static const backend_state_t const printer_states[] =
130 {
131 { CUPS_TC_lowPaper, "media-low-report" },
132 { CUPS_TC_noPaper | CUPS_TC_inputTrayEmpty, "media-empty-warning" },
133 { CUPS_TC_lowToner, "toner-low-report" },
134 { CUPS_TC_noToner, "toner-empty-warning" },
135 { CUPS_TC_doorOpen, "door-open-report" },
136 { CUPS_TC_jammed, "media-jam-warning" },
137 /* { CUPS_TC_offline, "offline-report" }, */ /* unreliable */
e07d4801 138 /* { CUPS_TC_serviceRequested | CUPS_TC_overduePreventMaint, "service-needed-warning" }, */ /* unreliable */
745129be
MS
139 { CUPS_TC_inputTrayMissing, "input-tray-missing-warning" },
140 { CUPS_TC_outputTrayMissing, "output-tray-missing-warning" },
141 { CUPS_TC_markerSupplyMissing, "marker-supply-missing-warning" },
142 { CUPS_TC_outputNearFull, "output-area-almost-full-report" },
143 { CUPS_TC_outputFull, "output-area-full-warning" }
144 };
145
568fa3fa
MS
146
147/*
148 * Local functions...
149 */
150
151static void backend_init_supplies(int snmp_fd, http_addr_t *addr);
152static void backend_walk_cb(cups_snmp_t *packet, void *data);
153
154
155/*
156 * 'backendSNMPSupplies()' - Get the current supplies for a device.
157 */
158
159int /* O - 0 on success, -1 on error */
160backendSNMPSupplies(
161 int snmp_fd, /* I - SNMP socket */
162 http_addr_t *addr, /* I - Printer address */
163 int *page_count, /* O - Page count */
164 int *printer_state) /* O - Printer state */
165{
166 if (!httpAddrEqual(addr, &current_addr))
167 backend_init_supplies(snmp_fd, addr);
168 else if (num_supplies > 0)
7a14d768 169 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
969307f0 170 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesLevel, 0.5,
568fa3fa
MS
171 backend_walk_cb, NULL);
172
173 if (page_count)
174 *page_count = -1;
175
176 if (printer_state)
177 *printer_state = -1;
178
179 if (num_supplies > 0)
180 {
745129be
MS
181 int i, /* Looping var */
182 new_state, /* New state value */
183 change_state; /* State change */
568fa3fa
MS
184 char value[CUPS_MAX_SUPPLIES * 4],
185 /* marker-levels value string */
186 *ptr; /* Pointer into value string */
187 cups_snmp_t packet; /* SNMP response packet */
745129be 188
568fa3fa
MS
189
190 /*
191 * Generate the marker-levels value string...
192 */
193
194 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
195 {
196 if (i)
197 *ptr++ = ',';
198
06d4e77b
MS
199 if (supplies[i].max_capacity > 0)
200 sprintf(ptr, "%d", 100 * supplies[i].level / supplies[i].max_capacity);
201 else
202 strcpy(ptr, "-1");
568fa3fa
MS
203 }
204
205 fprintf(stderr, "ATTR: marker-levels=%s\n", value);
206
207 /*
208 * Get the current printer status bits...
209 */
210
7a14d768
MS
211 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
212 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
568fa3fa
MS
213 hrPrinterDetectedErrorState))
214 return (-1);
215
969307f0 216 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
568fa3fa
MS
217 packet.object_type != CUPS_ASN1_OCTET_STRING)
218 return (-1);
219
745129be
MS
220 new_state = (packet.object_value.string.bytes[0] << 8) |
221 packet.object_value.string.bytes[1];
568fa3fa 222
745129be
MS
223 if (current_state < 0)
224 change_state = 0xffff;
568fa3fa 225 else
745129be 226 change_state = current_state ^ new_state;
568fa3fa 227
745129be
MS
228 for (i = 0;
229 i < (int)(sizeof(printer_states) / sizeof(printer_states[0]));
230 i ++)
231 if (change_state & printer_states[i].bit)
232 fprintf(stderr, "STATE: %c%s\n",
233 (new_state & printer_states[i].bit) ? '+' : '-',
234 printer_states[i].keyword);
568fa3fa 235
745129be 236 current_state = new_state;
568fa3fa
MS
237
238 /*
239 * Get the current printer state...
240 */
241
242 if (printer_state)
243 {
7a14d768
MS
244 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
245 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
568fa3fa
MS
246 hrPrinterStatus))
247 return (-1);
248
969307f0 249 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
568fa3fa
MS
250 packet.object_type != CUPS_ASN1_INTEGER)
251 return (-1);
252
253 *printer_state = packet.object_value.integer;
254 }
255
256 /*
257 * Get the current page count...
258 */
259
260 if (page_count)
261 {
7a14d768
MS
262 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
263 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
568fa3fa
MS
264 prtMarkerLifeCount))
265 return (-1);
266
969307f0 267 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
568fa3fa
MS
268 packet.object_type != CUPS_ASN1_COUNTER)
269 return (-1);
270
271 *page_count = packet.object_value.counter;
272 }
273
274 return (0);
275 }
276 else
277 return (-1);
278}
279
280
281/*
282 * 'backend_init_supplies()' - Initialize the supplies list.
283 */
284
285static void
286backend_init_supplies(
287 int snmp_fd, /* I - SNMP socket */
288 http_addr_t *addr) /* I - Printer address */
289{
290 int i, /* Looping var */
291 type; /* Current marker type */
292 cups_file_t *cachefile; /* Cache file */
293 const char *cachedir; /* CUPS_CACHEDIR value */
294 char addrstr[1024], /* Address string */
295 cachefilename[1024], /* Cache filename */
296 description[CUPS_SNMP_MAX_STRING],
297 /* Device description string */
298 value[CUPS_MAX_SUPPLIES * (CUPS_SNMP_MAX_STRING * 2 + 3)],
299 /* Value string */
300 *ptr, /* Pointer into value string */
301 *name_ptr; /* Pointer into name string */
302 cups_snmp_t packet; /* SNMP response packet */
58dc1933
MS
303 ppd_file_t *ppd; /* PPD file for this queue */
304 ppd_attr_t *ppdattr; /* cupsSNMPSupplies attribute */
568fa3fa
MS
305 static const char * const types[] = /* Supply types */
306 {
307 "other",
308 "unknown",
309 "toner",
310 "wasteToner",
311 "ink",
312 "inkCartridge",
313 "inkRibbon",
314 "wasteInk",
315 "opc",
316 "developer",
317 "fuserOil",
318 "solidWax",
319 "ribbonWax",
320 "wasteWax",
321 "fuser",
322 "coronaWire",
323 "fuserOilWick",
324 "cleanerUnit",
325 "fuserCleaningPad",
326 "transferUnit",
327 "tonerCartridge",
328 "fuserOiler",
329 "water",
330 "wasteWater",
331 "glueWaterAdditive",
332 "wastePaper",
333 "bindingSupply",
334 "bandingSupply",
335 "stitchingWire",
336 "shrinkWrap",
337 "paperWrap",
338 "staples",
339 "inserts",
340 "covers"
341 };
342
343
344 /*
345 * Reset state information...
346 */
347
745129be
MS
348 current_addr = *addr;
349 current_state = -1;
350 num_supplies = -1;
568fa3fa
MS
351
352 memset(supplies, 0, sizeof(supplies));
353
58dc1933
MS
354 /*
355 * See if we should be getting supply levels via SNMP...
356 */
357
358 if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL &&
359 (ppdattr = ppdFindAttr(ppd, "cupsSNMPSupplies", NULL)) != NULL &&
360 ppdattr->value && strcasecmp(ppdattr->value, "true"))
361 {
362 ppdClose(ppd);
363 return;
364 }
365
366 ppdClose(ppd);
367
568fa3fa
MS
368 /*
369 * Get the device description...
370 */
371
7a14d768
MS
372 if (!_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
373 _cupsSNMPDefaultCommunity(), CUPS_ASN1_GET_REQUEST, 1,
568fa3fa
MS
374 hrDeviceDescr))
375 return;
376
969307f0 377 if (!_cupsSNMPRead(snmp_fd, &packet, 0.5) ||
568fa3fa
MS
378 packet.object_type != CUPS_ASN1_OCTET_STRING)
379 {
380 strlcpy(description, "Unknown", sizeof(description));
381 num_supplies = 0;
382 }
383 else
d1c13e16
MS
384 strlcpy(description, (char *)packet.object_value.string.bytes,
385 sizeof(description));
568fa3fa
MS
386
387 /*
388 * See if we have already queried this device...
389 */
390
391 httpAddrString(addr, addrstr, sizeof(addrstr));
392
393 if ((cachedir = getenv("CUPS_CACHEDIR")) == NULL)
394 cachedir = CUPS_CACHEDIR;
395
396 snprintf(cachefilename, sizeof(cachefilename), "%s/%s.snmp", cachedir,
397 addrstr);
398
399 if ((cachefile = cupsFileOpen(cachefilename, "r")) != NULL)
400 {
401 /*
402 * Yes, read the cache file:
403 *
404 * 1 num_supplies
405 * device description
406 * supply structures...
407 */
408
409 if (cupsFileGets(cachefile, value, sizeof(value)))
410 {
411 if (sscanf(value, "1 %d", &num_supplies) == 1 &&
412 num_supplies <= CUPS_MAX_SUPPLIES &&
413 cupsFileGets(cachefile, value, sizeof(value)))
414 {
415 if ((ptr = value + strlen(value) - 1) >= value && *ptr == '\n')
416 *ptr = '\n';
417
418 if (!strcmp(description, value))
419 cupsFileRead(cachefile, (char *)supplies,
420 num_supplies * sizeof(backend_supplies_t));
421 else
422 num_supplies = -1;
423 }
424 else
425 num_supplies = -1;
426 }
427
428 cupsFileClose(cachefile);
429 }
430
431 /*
432 * If the cache information isn't correct, scan for supplies...
433 */
434
435 if (num_supplies < 0)
436 {
437 /*
438 * Walk the printer configuration information...
439 */
440
7a14d768 441 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
969307f0 442 _cupsSNMPDefaultCommunity(), prtMarkerSuppliesEntry, 0.5,
568fa3fa
MS
443 backend_walk_cb, NULL);
444 }
445
446 /*
447 * Save the cached information...
448 */
449
450 if (num_supplies < 0)
451 num_supplies = 0;
452
453 if ((cachefile = cupsFileOpen(cachefilename, "w")) != NULL)
454 {
455 cupsFilePrintf(cachefile, "1 %d\n", num_supplies);
456 cupsFilePrintf(cachefile, "%s\n", description);
457
458 if (num_supplies > 0)
459 cupsFileWrite(cachefile, (char *)supplies,
460 num_supplies * sizeof(backend_supplies_t));
461
462 cupsFileClose(cachefile);
463 }
464
465 if (num_supplies <= 0)
466 return;
467
468 /*
469 * Get the colors...
470 */
471
472 for (i = 0; i < num_supplies; i ++)
473 strcpy(supplies[i].color, "none");
474
7a14d768 475 _cupsSNMPWalk(snmp_fd, &current_addr, CUPS_SNMP_VERSION_1,
969307f0 476 _cupsSNMPDefaultCommunity(), prtMarkerColorantValue, 0.5,
568fa3fa
MS
477 backend_walk_cb, NULL);
478
479 /*
480 * Output the marker-colors attribute...
481 */
482
483 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
484 {
485 if (i)
486 *ptr++ = ',';
487
488 strcpy(ptr, supplies[i].color);
489 }
490
491 fprintf(stderr, "ATTR: marker-colors=%s\n", value);
492
493 /*
494 * Output the marker-names attribute...
495 */
496
497 for (i = 0, ptr = value; i < num_supplies; i ++)
498 {
499 if (i)
500 *ptr++ = ',';
501
502 *ptr++ = '\"';
503 for (name_ptr = supplies[i].name; *name_ptr;)
504 {
505 if (*name_ptr == '\\' || *name_ptr == '\"')
506 *ptr++ = '\\';
507
508 *ptr++ = *name_ptr++;
509 }
510 *ptr++ = '\"';
511 }
512
513 *ptr = '\0';
514
515 fprintf(stderr, "ATTR: marker-names=%s\n", value);
516
517 /*
518 * Output the marker-types attribute...
519 */
520
521 for (i = 0, ptr = value; i < num_supplies; i ++, ptr += strlen(ptr))
522 {
523 if (i)
524 *ptr++ = ',';
525
526 type = supplies[i].type;
527
528 if (type < CUPS_TC_other || type > CUPS_TC_covers)
529 strcpy(ptr, "unknown");
530 else
531 strcpy(ptr, types[type - CUPS_TC_other]);
532 }
533
534 fprintf(stderr, "ATTR: marker-types=%s\n", value);
535}
536
537
538/*
539 * 'backend_walk_cb()' - Interpret the supply value responses...
540 */
541
542static void
543backend_walk_cb(cups_snmp_t *packet, /* I - SNMP packet */
544 void *data) /* I - User data (unused) */
545{
546 int i, j, k; /* Looping vars */
547 static const char * const colors[8][2] =
548 { /* Standard color names */
549 { "black", "#000000" },
550 { "blue", "#0000FF" },
551 { "cyan", "#00FFFF" },
552 { "green", "#00FF00" },
553 { "magenta", "#FF00FF" },
554 { "red", "#FF0000" },
555 { "white", "#FFFFFF" },
556 { "yellow", "#FFFF00" }
557 };
558
559
560 (void)data;
561
7a14d768 562 if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerColorantValue) &&
568fa3fa
MS
563 packet->object_type == CUPS_ASN1_OCTET_STRING)
564 {
565 /*
566 * Get colorant...
567 */
568
569 i = packet->object_name[prtMarkerColorantValueOffset];
570
571 fprintf(stderr, "DEBUG2: prtMarkerColorantValue.1.%d = \"%s\"\n", i,
d1c13e16 572 (char *)packet->object_value.string.bytes);
568fa3fa
MS
573
574 for (j = 0; j < num_supplies; j ++)
575 if (supplies[j].colorant == i)
576 {
577 for (k = 0; k < (int)(sizeof(colors) / sizeof(colors[0])); k ++)
d1c13e16 578 if (!strcmp(colors[k][0], (char *)packet->object_value.string.bytes))
568fa3fa
MS
579 {
580 strcpy(supplies[j].color, colors[k][1]);
581 break;
582 }
583 }
584 }
7a14d768 585 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesColorantIndex))
568fa3fa
MS
586 {
587 /*
588 * Get colorant index...
589 */
590
591 i = packet->object_name[prtMarkerSuppliesColorantIndexOffset];
592 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
593 packet->object_type != CUPS_ASN1_INTEGER)
594 return;
595
596 fprintf(stderr, "DEBUG2: prtMarkerSuppliesColorantIndex.1.%d = %d\n", i,
597 packet->object_value.integer);
598
599 if (i > num_supplies)
600 num_supplies = i;
601
602 supplies[i - 1].colorant = packet->object_value.integer;
603 }
7a14d768 604 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesDescription))
568fa3fa
MS
605 {
606 /*
607 * Get supply name/description...
608 */
609
610 i = packet->object_name[prtMarkerSuppliesDescriptionOffset];
611 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
612 packet->object_type != CUPS_ASN1_OCTET_STRING)
613 return;
614
615 fprintf(stderr, "DEBUG2: prtMarkerSuppliesDescription.1.%d = \"%s\"\n", i,
d1c13e16 616 (char *)packet->object_value.string.bytes);
568fa3fa
MS
617
618 if (i > num_supplies)
619 num_supplies = i;
620
d1c13e16 621 strlcpy(supplies[i - 1].name, (char *)packet->object_value.string.bytes,
568fa3fa
MS
622 sizeof(supplies[0].name));
623 }
7a14d768 624 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesLevel))
568fa3fa
MS
625 {
626 /*
627 * Get level...
628 */
629
630 i = packet->object_name[prtMarkerSuppliesLevelOffset];
631 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
632 packet->object_type != CUPS_ASN1_INTEGER)
633 return;
634
635 fprintf(stderr, "DEBUG2: prtMarkerSuppliesLevel.1.%d = %d\n", i,
636 packet->object_value.integer);
637
638 if (i > num_supplies)
639 num_supplies = i;
640
641 supplies[i - 1].level = packet->object_value.integer;
642 }
7a14d768 643 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesMaxCapacity))
568fa3fa
MS
644 {
645 /*
646 * Get max capacity...
647 */
648
649 i = packet->object_name[prtMarkerSuppliesMaxCapacityOffset];
650 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
651 packet->object_type != CUPS_ASN1_INTEGER)
652 return;
653
654 fprintf(stderr, "DEBUG2: prtMarkerSuppliesMaxCapacity.1.%d = %d\n", i,
655 packet->object_value.integer);
656
657 if (i > num_supplies)
658 num_supplies = i;
659
660 supplies[i - 1].max_capacity = packet->object_value.integer;
661 }
7a14d768 662 else if (_cupsSNMPIsOIDPrefixed(packet, prtMarkerSuppliesType))
568fa3fa
MS
663 {
664 /*
665 * Get marker type...
666 */
667
668 i = packet->object_name[prtMarkerSuppliesTypeOffset];
669 if (i < 1 || i > CUPS_MAX_SUPPLIES ||
670 packet->object_type != CUPS_ASN1_INTEGER)
671 return;
672
673 fprintf(stderr, "DEBUG2: prtMarkerSuppliesType.1.%d = %d\n", i,
674 packet->object_value.integer);
675
676 if (i > num_supplies)
677 num_supplies = i;
678
679 supplies[i - 1].type = packet->object_value.integer;
680 }
681}
682
683
684/*
685 * End of "$Id$".
686 */