- add ipv6.addr for the source and destination addresses (like ipv4)
[obnox/wireshark/wip.git] / ncp2222.py
1 #!/usr/bin/python
2
3 """
4 Creates C code from a table of NCP type 0x2222 packet types.
5 (And 0x3333, which are the replies, but the packets are more commonly
6 refered to as type 0x2222; the 0x3333 replies are understood to be
7 part of the 0x2222 "family")
8
9 Data comes from "Programmer's Guide to the NetWare Core Protocol"
10 by Steve Conner and Dianne Conner.
11
12 $Id: ncp2222.py,v 1.3 2000/08/09 21:24:27 deniel Exp $
13
14 Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
15
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the GNU General Public License
18 as published by the Free Software Foundation; either version 2
19 of the License, or (at your option) any later version.
20  
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 GNU General Public License for more details.
25  
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29 """
30
31 import sys
32
33 ##############################################################################
34 # Global containers
35 ##############################################################################
36
37 class UniqueCollection:
38         """The UniqueCollection class stores objects which can be compared to other
39         objects of the same class. If two objects in the collection are equivalent,
40         only one is stored."""
41
42         def __init__(self, name):
43                 "Constructor"
44                 self.name = name
45                 self.members = []
46
47         def Add(self, object):
48                 """Add an object to the members lists, if a comparable object
49                 doesn't already exist. The object that is in the member list, that is
50                 either the object that was added or the comparable object that was
51                 already in the member list, is returned."""
52
53                 # Is 'object' a duplicate of some other member?
54                 for member in self.members:
55                         if member == object:
56                                 return member
57
58                 # Store object in our members list.
59                 self.members.append(object)
60                 return object
61
62         def Members(self):
63                 "Returns the list of members."
64                 return self.members
65
66         def HasMember(self, object):
67                 "Does the list of members contain the object?"
68                 for member in self.members:
69                         if member == object:
70                                 return 1
71                 return 0
72
73
74 packets         = UniqueCollection('NCP Packet Descriptions')
75 compcode_lists  = UniqueCollection('Completion Code Lists')
76 ptvc_lists      = UniqueCollection('PTVC Lists')
77
78
79 ##############################################################################
80
81 class NamedList:
82         "NamedList's keep track of PTVC's and Completion Codes"
83         def __init__(self, name, list):
84                 "Constructor"
85                 self.name = name
86                 self.list = list
87
88         def __cmp__(self, other):
89                 "Compare this NamedList to another"
90
91                 # Python will do a deep comparison of lists within lists.
92                 if self.list < other.list:
93                         return -1
94                 elif self.list > other.list:
95                         return 1
96                 else:
97                         return 0
98
99         def __repr__(self):
100                 "String representation"
101                 return "NamedList: " + `self.list`
102
103         def Name(self, new_name = None):
104                 "Get/Set name of list"
105                 if new_name != None:
106                         self.name = new_name
107                 return self.name
108
109         def Records(self):
110                 "Returns record lists"
111                 return self.list
112
113         def Null(self):
114                 "Is there no list (different from an empty list)?"
115                 return self.list == None
116
117         def Empty(self):
118                 "It the list empty (different from a null list)?"
119                 assert(not self.Null())
120
121                 if self.list:
122                         return 0
123                 else:
124                         return 1
125
126
127 class PTVC(NamedList):
128         """ProtoTree TVBuff Cursor List ("PTVC List") Class"""
129
130         def __init__(self, name, records):
131                 "Constructor"
132                 self.list = []
133                 NamedList.__init__(self, name, self.list)
134
135                 # Make a PTVCRecord object for each list in 'records'
136                 for record in records:
137                         ptvc_rec = PTVCRecord(record)
138         
139                         # We can't make a PTVC list from a variable-length
140                         # packet. XXX - unless it's FT_NSTRING
141 #                       if type(ptvc_rec.Length()) == type(()):
142 #                               if ptvc_rec.Field() == nstring8:
143 #                                       pass
144 #                               else:
145 #                                       self.list = None
146 #                                       return
147
148                         self.list.append(ptvc_rec)
149
150 class PTVCRecord:
151         def __init__(self, record):
152                 "Constructor"
153                 self.length     = record[1]
154                 self.field      = record[2]
155
156                 # Small sanity check
157                 field_length = self.field.Length()
158
159                 if type(field_length) == type(0) and field_length > 0:
160                         if field_length != self.length:
161                                 sys.stderr.write("Length %d does not match field length %d for field %s\n" % (self.length, field_length, self.field.Abbreviation()))
162                                 sys.exit(1)
163
164                 # Check if an endianness override is given
165                 try:
166                         self.endianness = record[3]
167
168                 # If no endianness was given in the record, then
169                 # use the field's default endianness.
170                 except IndexError:
171                         self.endianness = self.field.Endianness()
172
173         def __cmp__(self, other):
174                 "Comparison operator"
175                 if self.length < other.length:
176                         return -1
177                 elif self.length > other.length:
178                         return 1
179
180                 if self.field != other.field:
181                         return 1
182                 elif self.endianness != other.endianness:
183                         return 1
184                 else:
185                         return 0
186
187         def __repr__(self):
188                 "String representation"
189                 endianness = 'FALSE'
190                 if self.endianness == LE:
191                         endianness = 'TRUE'
192
193                 if type(self.length) == type(0):
194                         length = self.length
195                         return "{ &%s, %d, %s }" % (self.field.HFName(),
196                                         length, endianness)
197                 else:
198                         length = "PTVC_VARIABLE_LENGTH"
199                         return "{ &%s, %s, %s }" % (self.field.HFName(),
200                                         length, endianness)
201
202
203         def Length(self):
204                 return self.length
205
206         def Field(self):
207                 return self.field
208
209
210 ##############################################################################
211
212 class NCP:
213         "NCP Packet class"
214         def __init__(self, func_code, description, group):
215                 "Constructor"
216                 self.func_code          = func_code
217                 self.description        = description
218                 self.group              = group
219                 self.codes              = None
220                 self.request_records    = None
221                 self.reply_records      = None
222
223                 if not groups.has_key(group):
224                         sys.stderr.write("NCP 0x%x has invalid group '%s'\n" % (self.func_code, group))
225                         sys.exit(1)
226
227                 if self.HasSubFunction():
228                         # NCP Function with SubFunction
229                         self.start_offset = 10
230                 else:
231                         # Simple NCP Function
232                         self.start_offset = 7
233
234         def FunctionCode(self, part=None):
235                 "Returns the function code for this NCP packet."
236                 if part == None:
237                         return self.func_code
238                 elif part == 'high':
239                         if self.HasSubFunction():
240                                 return (self.func_code & 0xff00) >> 8
241                         else:
242                                 return self.func_code
243                 elif part == 'low':
244                         if self.HasSubFunction():
245                                 return self.func_code & 0x00ff
246                         else:
247                                 return 0x00
248                 else:
249                         sys.stderr.write("Unknown directive '%s' for function_code()\n" % (part))
250                         sys.exit(1)
251
252         def HasSubFunction(self):
253                 "Does this NPC packet require a subfunction field?"
254                 if self.func_code <= 0xff:
255                         return 0
256                 else:
257                         return 1
258
259         def Description(self):
260                 return self.description
261
262         def Group(self):
263                 return self.group
264
265         def PTVCRequest(self):
266                 return self.ptvc_request
267
268         def PTVCReply(self):
269                 return self.ptvc_reply
270
271         def Request(self, size, records=[]):
272                 self.request_size = size
273                 self.request_records = records
274                 if self.HasSubFunction():
275                         self.CheckRecords(size, records, "Request", 10)
276                 else:
277                         self.CheckRecords(size, records, "Request", 7)
278                 self.ptvc_request = self.MakePTVC(records, "request")
279
280         def Reply(self, size, records=[]):
281                 self.reply_size = size
282                 self.reply_records = records
283                 self.CheckRecords(size, records, "Reply", 8)
284                 self.ptvc_reply = self.MakePTVC(records, "reply")
285
286         def CheckRecords(self, size, records, descr, min_hdr_length):
287                 "Simple sanity check"
288                 min = size
289                 max = size
290                 if type(size) == type(()):
291                         min = size[0]
292                         max = size[1]
293
294                 lower = min_hdr_length
295                 upper = min_hdr_length
296
297                 for record in records:
298                         rec_size = record[1]
299                         rec_lower = rec_size
300                         rec_upper = rec_size
301                         if type(rec_size) == type(()):
302                                 rec_lower = rec_size[0]
303                                 rec_upper = rec_size[1]
304
305                         lower = lower + rec_lower
306                         upper = upper + rec_upper
307
308                 error = 0
309                 if min != lower:
310                         sys.stderr.write("%s records for 2222/0x%x sum to %d bytes minimum, but param1 shows %d\n" \
311                                 % (descr, self.FunctionCode(), lower, min))
312                         error = 1
313                 if max != upper:
314                         sys.stderr.write("%s records for 2222/0x%x sum to %d bytes maximum, but param1 shows %d\n" \
315                                 % (descr, self.FunctionCode(), upper, max))
316                         error = 1
317
318                 if error == 1:
319                         sys.exit(1)
320
321
322         def MakePTVC(self, records, name_suffix):
323                 """Makes a PTVC out of a request or reply record list. Possibly adds
324                 it to the global list of PTVCs (the global list is a UniqueCollection,
325                 so an equivalent PTVC may already be in the global list)."""
326
327                 name = "%s_%s" % (self.CName(), name_suffix)
328                 ptvc = PTVC(name, records)
329                 return ptvc_lists.Add(ptvc)
330
331         def CName(self):
332                 "Returns a C symbol based on the NCP function code"
333                 return "ncp_0x%x" % (self.func_code)
334
335         def Variables(self):
336                 """Returns a list of variables used in the request and reply records.
337                 A variable is listed only once, even if it is used twice (once in
338                 the request, once in the reply)."""
339
340                 variables = {}
341                 if self.request_records:
342                         for record in self.request_records:
343                                 var = record[2]
344                                 variables[var] = 1
345
346                 if self.reply_records:
347                         for record in self.reply_records:
348                                 var = record[2]
349                                 variables[var] = 1
350
351                 return variables.keys()
352
353
354         def CompletionCodes(self, codes=None):
355                 """Sets or returns the list of completion codes. Internally, a NamedList
356                 is used to store the completion codes, but the caller of this function
357                 never realizes that because Python lists are the input and output."""
358
359                 if codes == None:
360                         return self.codes
361
362                 # Sanity check
363                 okay = 1
364                 for code in codes:
365                         if not errors.has_key(code):
366                                 sys.stderr.write("Errors table does not have key 0x%04x for NCP=0x%x\n" % (code,
367                                         self.func_code))
368                                 okay = 0
369
370                 # Delay the exit until here so that the programmer can get the complete
371                 # list of missing error codes
372                 if not okay:
373                         sys.exit(1)
374
375                 # Create CompletionCode (NamedList) object and possible add it to
376                 # the global list of completion code lists.
377                 name = "%s_errors" % (self.CName())
378                 codes.sort()
379                 codes_list = NamedList(name, codes)
380                 self.codes = compcode_lists.Add(codes_list)
381
382                 self.Finalize()
383
384         def Finalize(self):
385                 """Adds the NCP object to the global collection of NCP objects. This
386                 is done automatically after setting the CompletionCode list. Yes, this
387                 is a shortcut, but it makes our list of NCP packet definitions look
388                 neater, since an explicit "add to global list of packets" is not needed."""
389
390                 # Add packet to global collection of packets
391                 if packets.HasMember(self):
392                         sys.stderr.write("Already have NCP Function Code 0x%x\n" % \
393                                 (self.func_code))
394                         sys.exit(1)
395                 else:
396                         packets.Add(self)
397
398
399
400 ##############################################################################
401
402 LE              = 1             # Little-Endian
403 BE              = 0             # Big-Endian
404 NA              = -1            # Not Applicable
405
406 class Type:
407         " Virtual class for NCP field types"
408         type            = "Type"
409         ftype           = None
410         disp            = "BASE_DEC"
411         endianness      = NA
412         values          = []
413
414         def __init__(self, abbrev, descr, bytes, endianness = NA):
415                 self.abbrev = abbrev
416                 self.descr = descr
417                 self.bytes = bytes
418
419         def Length(self):
420                 return self.bytes
421
422         def Abbreviation(self):
423                 return self.abbrev
424
425         def Description(self):
426                 return self.descr
427
428         def HFName(self):
429                 return "hf_ncp_" + self.abbrev
430
431         def DFilter(self):
432                 return "ncp." + self.abbrev
433
434         def EtherealFType(self):
435                 return self.ftype
436
437         def Display(self, newval=None):
438                 if newval != None:
439                         self.disp = newval
440                 return self.disp
441
442         def ValuesName(self):
443                 return "NULL"
444
445         def Mask(self):
446                 return 0
447
448         def Endianness(self):
449                 return self.endianness
450
451 class byte(Type):
452         type    = "byte"
453         ftype   = "FT_UINT8"
454         def __init__(self, abbrev, descr):
455                 Type.__init__(self, abbrev, descr, 1)
456
457 # Same as above. Both are provided for convenience
458 class uint8(Type):
459         type    = "uint8"
460         ftype   = "FT_UINT8"
461         def __init__(self, abbrev, descr):
462                 Type.__init__(self, abbrev, descr, 1)
463
464 class uint16(Type):
465         type    = "uint16"
466         ftype   = "FT_UINT16"
467         def __init__(self, abbrev, descr, endianness = BE):
468                 Type.__init__(self, abbrev, descr, 2, endianness)
469
470 class uint32(Type):
471         type    = "uint32"
472         ftype   = "FT_UINT32"
473         def __init__(self, abbrev, descr, endianness = BE):
474                 Type.__init__(self, abbrev, descr, 4, endianness)
475
476 class nstring8(Type):
477         """A string of up to 255 characters. The first byte
478         gives the string length. Thus, the total length of
479         this data structure is from 1 to 256 bytes, including
480         the first byte."""
481
482         type    = "nstring8"
483         ftype   = "FT_NSTRING_UINT8"
484         def __init__(self, abbrev, descr):
485                 Type.__init__(self, abbrev, descr, -1)
486
487 class stringz(Type):
488         "NUL-terminated string."
489
490         type    = "stringz"
491         ftype   = "FT_STRING"
492         def __init__(self, abbrev, descr):
493                 Type.__init__(self, abbrev, descr, -1)
494
495 class val_string(Type):
496         """Abstract class for val_stringN, where N is number
497         of bits that key takes up."""
498
499         type    = "val_string"
500         disp    = 'BASE_HEX'
501
502         def __init__(self, abbrev, descr, val_string_array, endianness = BE):
503                 Type.__init__(self, abbrev, descr, self.bytes, endianness)
504                 self.values = val_string_array
505
506         def __repr__(self):
507                 result = "static const value_string %s[] = {\n" \
508                                 % (self.ValuesCName())
509                 for val_record in self.values:
510                         value   = val_record[0]
511                         text    = val_record[1]
512                         value_repr = self.value_format % value
513                         result = result + '\t{ %s,\t"%s" },\n' \
514                                         % (value_repr, text)
515
516                 value_repr = self.value_format % 0
517                 result = result + "\t{ %s,\tNULL },\n" % (value_repr)
518                 result = result + "};\n"
519
520                 return result
521
522         def ValuesCName(self):
523                 return "ncp_%s_vals" % (self.abbrev)
524
525         def ValuesName(self):
526                 return "VALS(%s)" % (self.ValuesCName())
527
528 class val_string8(val_string):
529         type            = "val_string8"
530         ftype           = "FT_UINT8"
531         bytes           = 1
532         value_format    = "0x%02x"
533
534 class val_string16(val_string):
535         type            = "val_string16"
536         ftype           = "FT_UINT16"
537         bytes           = 2
538         value_format    = "0x%04x"
539
540 class bytes(Type):
541         type    = 'bytes'
542         ftype   = 'FT_BYTES'
543
544         def __init__(self, abbrev, descr, bytes):
545                 Type.__init__(self, abbrev, descr, bytes, NA)
546
547 #class data(Type):
548 #       type    = "data"
549 #       ftype   = "FT_BYTES"
550 #       def __init__(self, abbrev, descr):
551 #               Type.__init__(self, abbrev, descr, -1)
552 #
553 #       def length_var(self, length_var):
554 #               self.length_var = length_var
555
556 ##############################################################################
557 # NCP Field Types. Defined in Appendix A of "Programmer's Guide..."
558 ##############################################################################
559 BufferSize      = uint16("buffer_size", "Buffer Size")
560 ConnectionNumber        = uint32("connection_number", "Connection Number")
561 DirHandle       = byte("dir_handle", "Directory Handle")
562
563 FileHandle      = bytes("file_handle", "File Handle", 6)
564
565 FileLock        = val_string8("file_lock", "File Lock", [
566         [ 0x00, "Not Locked" ],
567         [ 0xfe, "Locked by file lock" ],
568         [ 0xff, "Unknown" ],
569 ])
570
571 FileOffset      = uint32("file_offset", "File Offset")
572 FilePath        = nstring8("file_path", "File Path")
573 FileSize        = uint32("file_size", "File Size")
574 JobType         = uint16("job_type", "Job Type")
575
576 LogicalLockType = val_string8("logical_lock_type", "Logical Lock Type", [
577         [ 0x00, "Log file" ],
578         [ 0x01, "Log and lock file for exclusive read/write use" ],
579         [ 0x03, "Log and lock with shareable read-only use" ],
580 ])
581
582 LogicalRecordName       = nstring8("logical_record_name", "Logical Record Name")
583 LogLockType     = byte("log_lock_type", "Log Lock Type")
584
585 MaxBytes        = uint16("max_bytes", "Maximum Number of Bytes")
586 NumBytes        = uint16("num_bytes", "Number of Bytes")
587
588 ObjectFlags     = val_string8("object_flags", "Object Flags", [
589         [ 0x00, "Dynamic object" ],
590         [ 0x01, "Static object" ],
591 ])
592
593 ObjectHasProperties = val_string8("object_has_properites", "Object Has Properties", [
594         [ 0x00, "No properties" ],
595         [ 0xff, "One or more properties" ],
596 ])
597
598 ObjectID        = uint32("object_id", "Object ID")
599 ObjectID.Display('BASE_HEX')
600
601 ObjectName      = nstring8("object_name", "Object Name")
602 ObjectNameZ     = stringz("object_nameZ", "Object Name")
603
604 ObjectSecurity  = val_string8("object_security", "Object Security", [
605         [ 0x00, "Anyone can read or modify the object" ],
606         [ 0x01, "Client logged into the file server can read the object" ],
607         [ 0x02, "Client logged into the file server with the object's name, type and password can read the object" ],
608         [ 0x03, "Client with supervisor equivalence can read the object" ],
609         [ 0x04, "Only the operating system can read the object" ],
610         [ 0x10, "Client logged into the file server can modify the object" ],
611         [ 0x20, "Client logged into the file server with the object's name, type and password can modify the object" ],
612         [ 0x30, "Client with supervisor equivalence can modify the object" ],
613         [ 0x40, "Only the operating system can modify the object" ],
614 ])
615
616 ObjectType      = val_string16("object_type", "Object Type", [
617         [ 0x0000,       "Unknown" ],
618         [ 0x0001,       "User" ],
619         [ 0x0002,       "User group" ],
620         [ 0x0003,       "Print queue" ],
621         [ 0x0004,       "NetWare file server" ],
622         [ 0x0005,       "Job server" ],
623         [ 0x0006,       "Gateway" ],
624         [ 0x0007,       "Print server" ],
625         [ 0x0008,       "Archive queue" ],
626         [ 0x0009,       "Archive server" ],
627         [ 0x000a,       "Job queue" ],
628         [ 0x000b,       "Administration" ],
629         [ 0x0021,       "NAS SNA gateway" ],
630         [ 0x0026,       "Remote bridge server" ],
631         [ 0x0027,       "TCP/IP gateway" ],
632 ])
633
634 PropertyHasMoreSegments = val_string8("property_has_more_segments",
635         "Property Has More Segments", [
636         [ 0x00, "Is last segment" ],
637         [ 0xff, "More segments are available" ],
638 ])
639
640 PropertyName    = nstring8("property_name", "Property Name")
641 PropertyData    = bytes("property_data", "Property Data", 128)
642 PropertySegment = uint8("property_segment", "Property Segment")
643
644 PropertyType    = val_string8("property_type", "Property Type", [
645         [ 0x00, "Static item" ],
646         [ 0x01, "Dynamic item" ],
647         [ 0x02, "Static set" ],
648         [ 0x03, "Dynamic set" ],
649 ])
650
651 TaskNumber      = uint32("task_number", "Task Number")
652 TimeoutLimit    = uint16("timeout_limit", "Timeout Limit")
653 UnknownByte     = byte("unknown_byte", "Unknown Byte")
654
655
656 ##############################################################################
657 # NCP Groups
658 ##############################################################################
659 groups = {}
660 groups['accounting']    = "Accounting"
661 groups['afp']           = "AFP"
662 groups['auditing']      = "Auditing"
663 groups['bindery']       = "Bindery"
664 groups['connection']    = "Connection"
665 groups['directory']     = "Directory"
666 groups['extended']      = "Extended Attribute"
667 groups['file']          = "File"
668 groups['fileserver']    = "File Server"
669 groups['message']       = "Message"
670 groups['migration']     = "Data Migration"
671 groups['misc']          = "Miscellaneous"
672 groups['name']          = "Name Space"
673 groups['nds']           = "NetWare Directory"
674 groups['print']         = "Print"
675 groups['queue']         = "Queue"
676 groups['sync']          = "Synchronization"
677 groups['tss']           = "Transaction Tracking"
678
679 ##############################################################################
680 # NCP Errors
681 ##############################################################################
682 errors = {}
683 errors[0x0000] = "Ok"
684 errors[0x0001] = "Transaction tracking is available"
685 errors[0x0002] = "Ok. The data has been written"
686
687 errors[0x0100] = "One or more of the ConnectionNumbers in the send list are invalid"
688 errors[0x0101] = "Invalid space limit"
689 errors[0x0102] = "Insufficient disk space"
690 errors[0x0103] = "Queue server cannot add jobs"
691 errors[0x0104] = "Out of disk space"
692 errors[0x0105] = "Semaphore overflow"
693
694 errors[0x0200] = "One or more clients in the send list are not logged in"
695 errors[0x0201] = "Queue server cannot attach"
696
697 errors[0x0300] = "One or more clients in the send list are not accepting messages"
698
699 errors[0x0400] = "Client already has message"
700 errors[0x0401] = "Queue server cannot service job"
701
702 errors[0x7e00] = "NCP failed boundary check"
703
704 errors[0x8000] = "Lock fail"
705 errors[0x8100] = "A file handle could not be allocated by the file server"
706 errors[0x8200] = "Unauthorized to open the file"
707 errors[0x8300] = "Unable to read/write the volume. Possible bad sector on the file server"
708
709 errors[0x8400] = "Unauthorized to create the directory"
710 errors[0x8401] = "Unauthorized to create the file"
711
712 errors[0x8500] = "Unauthorized to delete the specified file"
713 errors[0x8501] = "Unauthorized to overwrite an existing file in this directory"
714
715 errors[0x8700] = "An unexpected character was encountered in the filename"
716 errors[0x8800] = "Invalid file handle"
717 errors[0x8900] = "Unauthorized to search this directory"
718 errors[0x8a00] = "Unauthorized to delete this directory"
719 errors[0x8b00] = "Unauthorized to rename a file in this directory"
720
721 errors[0x8c00] = "No set privileges"
722 errors[0x8c01] = "Unauthorized to modify a file in this directory"
723 errors[0x8c02] = "Unauthorized to change the restriction on this volume"
724
725 errors[0x8d00] = "Some of the affected files are in use by another client"
726 errors[0x8d01] = "The affected file is in use"
727
728 errors[0x8e00] = "All of the affected files are in use by another client"
729 errors[0x8f00] = "Some of the affected files are read-only"
730
731 errors[0x9000] = "An attempt to modify a read-only volume occurred"
732 errors[0x9001] = "All of the affected files are read-only"
733
734 errors[0x9100] = "Some of the affected files already exist"
735
736 errors[0x9200] = "Directory with the new name already exists"
737 errors[0x9201] = "All of the affected files already exist"
738
739 errors[0x9300] = "Unauthorized to read from this file"
740 errors[0x9400] = "Unauthorized to write to this file"
741 errors[0x9500] = "The affected file is detached"
742
743 errors[0x9600] = "The file server has run out of memory to service this request"
744 errors[0x9601] = "No alloc space for message"
745
746 errors[0x9800] = "The affected volume is not mounted"
747 errors[0x9801] = "The volume associated with VolumeNumber is not mounted"
748 errors[0x9802] = "The resulting voume does not exist"
749 errors[0x9803] = "The destination volume is not mounted"
750
751 errors[0x9900] = "The file server has run out of directory space on the affected volume"
752 errors[0x9a00] = "The request attempted to rename the affected file to another volume"
753
754 errors[0x9b00] = "DirHandle is not associated with a valid directory path"
755 errors[0x9b01] = "A resulting directory handle is not associated with a valid directory path"
756 errors[0x9b02] = "The directory associated with DirHandle does not exist"
757 errors[0x9b03] = "Bad directory handle"
758
759 errors[0x9c00] = "The resulting path is not valid"
760 errors[0x9c01] = "The resulting file path is not valid"
761 errors[0x9c02] = "The resulting directory path is not valid"
762 errors[0x9c03] = "Invalid path"
763
764 errors[0x9d00] = "A directory handle was not available for allocation"
765
766 errors[0x9e00] = "The name of the directory does not conform to a legal name for this name space"
767 errors[0x9e01] = "The new directory name does not conform to a legal name for this name space"
768
769 errors[0x9f00] = "The request attempted to delete a directory that is in use by another client"
770
771 errors[0xa000] = "The request attempted to delete a directory that is not empty"
772 errors[0xa100] = "An unrecoverable error occured on the affected directory"
773 errors[0xa200] = "The request attempted to read from a file region that is physically locked"
774 errors[0xa400] = "Invalid directory rename attempted"
775
776 errors[0xbf00] = "Requests for this name space are not valid on this volume"
777
778 errors[0xc000] = "Unauthorized to retrieve accounting data"
779 errors[0xc100] = "The ACCOUNT_BALANCE property does not exist"
780 errors[0xc200] = "The object has exceeded its credit limit"
781 errors[0xc300] = "Too many holds have been placed against this account"
782 errors[0xc400] = "The client account has been disabled"
783
784 errors[0xc500] = "Access to the account has been denied because of intruder detection"
785 errors[0xc501] = "Login lockout"
786
787 errors[0xc600] = "The caller does not have operator priviliges"
788 errors[0xc601] = "The client does not have operator priviliges"
789
790 errors[0xd000] = "Queue error"
791 errors[0xd100] = "The queue does not exist"
792
793 errors[0xd200] = "A queue server is not associated with this queue"
794 errors[0xd201] = "A queue server is not associated with the selected queue"
795 errors[0xd202] = "No queue server"
796
797 errors[0xd300] = "No queue rights"
798
799 errors[0xd400] = "The queue is full and cannot accept another request"
800 errors[0xd401] = "The queue associated with ObjectId is full and cannot accept another request"
801
802 errors[0xd500] = "A job does not exist in this queue"
803 errors[0xd501] = "No queue job"
804 errors[0xd502] = "The job associated with JobNumber does not exist in this queue"
805
806 errors[0xd600] = "The file server does not allow unencrypted passwords"
807 errors[0xd601] = "No job right"
808
809 errors[0xd700] = "Bad account"
810 errors[0xd701] = "The old and new password strings are identical"
811 errors[0xd702] = "The job is currently being serviced"
812 errors[0xd703] = "The queue is currently servicing a job"
813 errors[0xd704] = "Queue servicing"
814
815 errors[0xd800] = "Queue not active"
816
817 errors[0xd900] = "The file server cannot accept another connection as it has reached its limit"
818 errors[0xd901] = "The client is not security equivalent to one of the objects in the Q_SERVERS group property of the target queue"
819 errors[0xd902] = "Station is not a server"
820
821 errors[0xda00] = "Attempted to login to the file server during a restricted time period"
822 errors[0xda01] = "Queue halted"
823
824 errors[0xdb00] = "Attempted to login to the file server from an unauthorized workstation or network"
825 errors[0xdb01] = "The queue cannot attach another queue server"
826 errors[0xdb02] = "Maximum queue servers"
827
828 errors[0xde00] = "Attempted to login to the file server with an incorrect password"
829 errors[0xdf00] = "Attempted to login to the file server with a password that has expired"
830
831 errors[0xe700] = "No disk track"
832 errors[0xe800] = "Write to group"
833 errors[0xe900] = "The object is already a member of the group property"
834
835 errors[0xea00] = "No such member"
836 errors[0xea01] = "The bindery object is not a member of the set"
837 errors[0xea02] = "Non-existent member"
838
839 errors[0xeb00] = "The property is not a set property"
840
841 errors[0xec00] = "No such set"
842 errors[0xec01] = "The set property does not exist"
843
844 errors[0xed00] = "Property exists"
845 errors[0xed01] = "The property already exists"
846 errors[0xed02] = "An attempt was made to create a bindery object property that already exists"
847
848 errors[0xee00] = "The object already exists"
849 errors[0xee01] = "The bindery object already exists"
850
851 errors[0xef00] = "Illegal name"
852 errors[0xef01] = "Illegal characters in ObjectName field"
853 errors[0xef02] = "Invalid name"
854
855 errors[0xf000] = "A wildcard was detected in a field that does not support wildcards"
856 errors[0xf001] = "An illegal wildcard was detected in ObjectName"
857
858 errors[0xf100] = "The client does not have the rights to access this bindery object"
859 errors[0xf101] = "Bindery security"
860 errors[0xf102] = "Invalid bindery security"
861
862 errors[0xf200] = "Unauthorized to read from this object"
863 errors[0xf300] = "Unauthorized to rename this object"
864
865 errors[0xf400] = "Unauthorized to delete this object"
866 errors[0xf401] = "No object delete privileges"
867 errors[0xf402] = "Unauthorized to delete this queue"
868
869 errors[0xf500] = "Unauthorized to create this object"
870 errors[0xf501] = "No object create"
871
872 errors[0xf600] = "No property delete"
873 errors[0xf601] = "Unauthorized to delete the property of this object"
874 errors[0xf602] = "Unauthorized to delete this property"
875
876 errors[0xf700] = "Unauthorized to create this property"
877 errors[0xf701] = "No property create privilege"
878
879 errors[0xf800] = "Unauthorized to write to this property"
880 errors[0xf900] = "Unauthorized to read this property"
881 errors[0xfa00] = "Temporary remap error"
882
883 errors[0xfb00] = "No such property"
884 errors[0xfb01] = "The file server does not support this request"
885 errors[0xfb02] = "The specified property does not exist"
886 errors[0xfb03] = "The PASSWORD property does not exist for this bindery object"
887
888 errors[0xfc00] = "The message queue cannot accept another message"
889 errors[0xfc01] = "The trustee associated with ObjectId does not exist"
890 errors[0xfc02] = "The specified bindery object does not exist"
891 errors[0xfc03] = "The bindery object associated with ObjectID does not exist"
892 errors[0xfc04] = "A bindery object does not exist that matches"
893 errors[0xfc05] = "The specified queue does not exist"
894 errors[0xfc06] = "No such object"
895 errors[0xfc07] = "The queue associated with ObjectID does not exist"
896
897 errors[0xfd00] = "Bad station number"
898 errors[0xfd01] = "The connection associated with ConnectionNumber is not active"
899 errors[0xfd02] = "Lock collision"
900 errors[0xfd03] = "Transacktion tracking is disabled"
901
902 errors[0xfe00] = "I/O failure"
903 errors[0xfe01] = "The files containing the bindery on the file server are locked"
904 errors[0xfe02] = "A file with the specified name already exists in this directory"
905 errors[0xfe03] = "No more restrictions were found"
906 errors[0xfe04] = "The file server was unable to lock the file within the specified time limit"
907 errors[0xfe05] = "The file server was unable to lock all files within the specified time limit"
908 errors[0xfe06] = "The bindery object associated with ObjectID is not a valid trustee"
909 errors[0xfe07] = "Directory locked"
910 errors[0xfe08] = "Bindery locked"
911 errors[0xfe09] = "Invalid semaphore name length"
912 errors[0xfe0a] = "The file server was unable to complete the operation within the specified time limit"
913 errors[0xfe0b] = "Transaction restart"
914
915 errors[0xff00] = "Failure"
916 errors[0xff01] = "Lock error"
917 errors[0xff02] = "File not found"
918 errors[0xff03] = "The file not found or cannot be unlocked"
919 errors[0xff04] = "Record not found"
920 errors[0xff05] = "The logical record was not found"
921 errors[0xff06] = "The printer associated with PrinterNumber does not exist"
922 errors[0xff07] = "No such printer"
923 errors[0xff08] = "Unable to complete the request"
924 errors[0xff09] = "Unauthorized to change privileges of this trustee"
925 errors[0xff0a] = "No files matching the search criteria were found"
926 errors[0xff0b] = "A file matching the search criteria was not found"
927 errors[0xff0c] = "Verification failed"
928 errors[0xff0d] = "Object associated with ObjectID is not a manager"
929 errors[0xff0e] = "Invalid initial semaphore value"
930 errors[0xff0f] = "The semaphore handle is not valid"
931 errors[0xff10] = "SemaphoreHandle is not associated with a valid sempahore"
932 errors[0xff11] = "Invalid semaphore handle"
933 errors[0xff12] = "Transaction tracking is not available"
934 errors[0xff13] = "The transaction has not yet been written to disk"
935 errors[0xff14] = "Directory already exists"
936 errors[0xff15] = "The file already exists and the deletion flag was not set"
937 errors[0xff16] = "No matching files or directories were found"
938 errors[0xff17] = "A file or directory matching the search criteria was not found"
939 errors[0xff18] = "The file already exists"
940 errors[0xff19] = "No files found"
941
942 ##############################################################################
943 # NCP Packets
944 ##############################################################################
945 # 2222/02
946 pkt = NCP(0x02, "File Release Lock", 'sync')
947 pkt.Request(7)
948 pkt.Reply(8)
949 pkt.CompletionCodes([0x0000, 0xff00])
950
951 #
952 # Untested
953 #
954 # 2222/03
955 #pkt = NCP(0x03, "Log File", 'sync')
956 #pkt.request( (12, 267), [
957 #       [ 7, 1, DirHandle ],
958 #       [ 8, 1, LogLockType ],
959 #       [ 9, 2, TimeoutLimit, LE ],
960 #       [ 11, (1, 256), FilePath ],
961 #       ])
962 #pkt.completion_codes([0x0000, 0x8200, 0x9600, 0xfe00, 0xff01])
963 #
964 ## 2222/04
965 #pkt = NCP(0x04, "Lock File Set", 'sync')
966 #pkt.request([
967 #       [ 7, TimeoutLimit ],
968 #       ])
969 #pkt.completion_codes([0xfe, 0xff01])
970 #
971 ## 2222/05
972 #pkt = NCP(0x05, "Release File", 'sync')
973 #pkt.request([
974 #       [ 7, DirHandle ],
975 #       [ 8, FilePath ],
976 #       ])
977 #pkt.completion_codes([0x7e, 0x98, 0x9b, 0x9c, 0xff02])
978 #
979 ## 2222/06
980 #pkt = NCP(0x06, "Release File Set", 'sync')
981 #pkt.request([
982 #       [ 7, UnknownByte ],
983 #       ])
984 #pkt.completion_codes()
985 #
986 ## 2222/07
987 #pkt = NCP(0x07, "Clear File", 'sync')
988 #pkt.request([
989 #       [ 7, DirHandle ],
990 #       [ 8, FilePath ],
991 #       ])
992 #pkt.completion_codes([0x7e, 0x96, 0x98, 0x9b, 0x9c,
993 #       0xa1, 0xfd, 0xff])
994 #
995 ## 2222/08
996 #pkt = NCP(0x08, "Clear File Set", 'sync')
997 #pkt.request([
998 #       [ 7, FileLock ],
999 #       ])
1000 #pkt.completion_codes([0x7e])
1001 #
1002 ## 2222/09
1003 #pkt = NCP(0x09, "Log Logical Record", 'sync')
1004 #pkt.request([
1005 #       [ 7, LogicalLockType ],
1006 #       [ 8, TimeoutLimit_be ],
1007 #       [ 10, LogicalRecordName ],
1008 #       ])
1009 #pkt.completion_codes([0x96, 0xfe, 0xff])
1010 #
1011 ## 2222/0a
1012 #pkt = NCP(0x0a, "Lock Logical Record Set", 'sync')
1013 #pkt.request([
1014 #       [ 7, LogicalLockType ],
1015 #       [ 8, TimeoutLimit_le ],
1016 #       ])
1017 #pkt.completion_codes([0xfe, 0xff])
1018 #
1019 ## 2222/0b
1020 #pkt = NCP(0x0b, "Clear Logical Record", 'sync')
1021 #pkt.request([
1022 #       [7, LogicalRecordName ],
1023 #       ])
1024 #pkt.completion_codes([0xff]
1025 ## 2222/0c
1026 ## 2222/0d
1027 ## 2222/0e
1028 ## 2222/0f
1029 ## 2222/11
1030 #
1031 ## 2222/1100
1032 #pkt = NCP(0x1100, "Lock Logical Record Set", 'sync')
1033 #pkt.request([
1034 #       [ 10, var_length_data("data").length_var("packetlength") ]
1035 #       ])
1036 #pkt.completion_codes()
1037 #
1038
1039 # 2222/1735
1040 pkt = NCP(0x1735, "Get Bindery Object ID", 'bindery')
1041 pkt.Request((13,60), [
1042         [ 10, 2, ObjectType ],
1043         [ 12, (1,48), ObjectName ],
1044 ])
1045 pkt.Reply(62, [
1046         [ 8, 4, ObjectID ],
1047         [ 12, 2, ObjectType ],
1048         [ 14, 48, ObjectName ], # XXX
1049 ])
1050 pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xf000, 0xfc02,
1051         0xfe01, 0xff00])
1052
1053 # 2222/1737
1054 pkt = NCP(0x1737, "Scan Bindery Object", 'bindery')
1055 pkt.Request((17,64), [
1056         [ 10, 4, ObjectID ],
1057         [ 14, 2, ObjectType ],
1058         [ 12, (1,48), ObjectName ],
1059 ])
1060 pkt.Reply(65, [
1061         [ 8, 4, ObjectID ],
1062         [ 12, 2, ObjectType ],
1063         [ 14, 48, ObjectNameZ ], # XXX
1064         [ 62, 1, ObjectFlags ],
1065         [ 63, 1, ObjectSecurity ],
1066         [ 64, 1, ObjectHasProperties ],
1067 ])
1068 pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xfc02,
1069         0xfe01, 0xff00])
1070
1071 # 2222/173D
1072 pkt = NCP(0x173D, "Read Property Value", 'bindery')
1073 pkt.Request((15,77), [
1074         [ 10, 2, ObjectType ],
1075         [ 12, (1,48), ObjectName ],
1076         [ -1, 1, PropertySegment ],
1077         [ -1, (1,16), PropertyName ],
1078 ])
1079 pkt.Reply(138, [
1080         [ 8, 128, PropertyData ],
1081         [ 136, 1, PropertyHasMoreSegments ],
1082         [ 137, 1, PropertyType ],
1083 ])
1084 pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0x9600, 0xec01,
1085         0xf000, 0xf100, 0xf900, 0xfb02, 0xfc02, 0xfe01, 0xff00 ])
1086
1087 # 2222/177C
1088 pkt = NCP(0x177C, "Service Queue Job", 'queue')
1089 pkt.Request(16, [
1090         [ 10, 4, ObjectID ],
1091         [ 14, 2, JobType ],
1092 ])
1093 pkt.Reply(24, [ # XXX - 76, [
1094         [ 8, 4, ConnectionNumber ],
1095         [ 12, 4, TaskNumber ],
1096         [ 16, 4, ObjectID ],
1097         [ 20, 4, ObjectID ],
1098         # XXX - DateTime
1099 ])
1100 # These completion codes are not documented, but guessed.
1101 pkt.CompletionCodes([0x0000, 0x9900, 0xd000, 0xd100, 0xd201, 0xd300,
1102         0xd401, 0xd502, 0xd601, 0xd704, 0xd800, 0xd901, 0xda01, 0xdb01,
1103         0xff00 ])
1104
1105 # 2222/18
1106 pkt = NCP(0x18, "End of Job", 'connection')
1107 pkt.Request(7)
1108 pkt.Reply(8)
1109 pkt.CompletionCodes([0x0000])
1110
1111 # 2222/19
1112 pkt = NCP(0x19, "Logout", 'connection')
1113 pkt.Request(7)
1114 pkt.Reply(8)
1115 pkt.CompletionCodes([0x0000])
1116
1117 # 2222/21
1118 pkt = NCP(0x21, "Negotiate Buffer Size", 'connection')
1119 pkt.Request(9, [
1120         [ 7, 2, BufferSize ],
1121 ])
1122 pkt.Reply(10, [
1123         [ 8, 2, BufferSize ],
1124 ])
1125 pkt.CompletionCodes([0x0000])
1126
1127 # 2222/42
1128 pkt = NCP(0x42, "Close File", 'file')
1129 pkt.Request(13, [
1130         [ 7, 6, FileHandle ],
1131 ])
1132 pkt.Reply(8)
1133 pkt.CompletionCodes([0x0000, 0xff00])
1134
1135 # 2222/47
1136 pkt = NCP(0x47, "Get Current Size of File", 'file')
1137 pkt.Request(13, [
1138         [ 7, 6, FileHandle ],
1139 ])
1140 pkt.Reply(12, [
1141         [ 8, 4, FileSize ],
1142 ])
1143 pkt.CompletionCodes([0x0000, 0x8800])
1144
1145 # 2222/48
1146 pkt = NCP(0x48, "Read From A File", 'file')
1147 pkt.Request(20, [
1148         [ 7, 1, UnknownByte ],
1149         [ 8, 6, FileHandle ],
1150         [ 14, 4, FileOffset ],  # my nomenclature
1151         [ 18, 2, MaxBytes ],    # my nomenclature
1152 ])
1153 pkt.Reply(10, [ # XXX - (10,-1), [
1154         [ 8, 2, NumBytes ],     # my nomenclature
1155         # XXX
1156 ])
1157 pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0xff00])
1158
1159 # 2222/5701     - no info
1160 # 2222/5702     - no info
1161 # 2222/5706     - no info
1162 # 2222/5714     - no info
1163 # 2222/68       - no info
1164 # 2222/72       - no info
1165
1166 ##############################################################################
1167 # Produce C code
1168 ##############################################################################
1169 if __name__ == '__main__':
1170         print "/*"
1171         print " * Generated automatically from %s" % (sys.argv[0])
1172         print " * Do not edit this file manually, as all changes will be lost."
1173         print " */\n"
1174
1175         print """
1176 /*
1177  * This program is free software; you can redistribute it and/or
1178  * modify it under the terms of the GNU General Public License
1179  * as published by the Free Software Foundation; either version 2
1180  * of the License, or (at your option) any later version.
1181  * 
1182  * This program is distributed in the hope that it will be useful,
1183  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1184  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1185  * GNU General Public License for more details.
1186  * 
1187  * You should have received a copy of the GNU General Public License
1188  * along with this program; if not, write to the Free Software
1189  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
1190  */
1191
1192 #ifdef HAVE_CONFIG_H
1193 # include "config.h"
1194 #endif
1195
1196 #include <glib.h>
1197 #include "packet.h"
1198 #include "conversation.h"
1199 #include "ptvcursor.h"
1200 #include "packet-ncp-int.h"
1201     
1202 static int hf_ncp_func = -1;
1203 static int hf_ncp_length = -1;
1204 static int hf_ncp_subfunc = -1;
1205 static int hf_ncp_completion_code = -1;
1206 static int hf_ncp_connection_status = -1;
1207         """
1208
1209         # Look at all packet types in the packets collection, and cull information
1210         # from them.
1211         packet_keys = []
1212         for packet in packets.Members():
1213                 packet_keys.append(packet.FunctionCode())
1214         packet_keys.sort()
1215
1216         errors_used_list = []
1217         errors_used_hash = {}
1218         groups_used_list = []
1219         groups_used_hash = {}
1220         variables_used_hash = {}
1221
1222         for pkt in packets.Members():
1223                 # Determine which error codes are used.
1224                 codes = pkt.CompletionCodes()
1225                 for code in codes.Records():
1226                         if not errors_used_hash.has_key(code):
1227                                 errors_used_hash[code] = len(errors_used_list)
1228                                 errors_used_list.append(code)
1229
1230                 # Determine which groups are used.
1231                 group = pkt.Group()
1232                 if not groups_used_hash.has_key(group):
1233                         groups_used_hash[group] = len(groups_used_list)
1234                         groups_used_list.append(group)
1235
1236                 # Determine which variables are used.
1237                 vars = pkt.Variables()
1238                 for var in vars:
1239                         variables_used_hash[var] = 1
1240
1241
1242
1243         # Print the hf variable declarations
1244         for var in variables_used_hash.keys():
1245                 print "static int " + var.HFName() + " = -1;"
1246
1247
1248         # Print the value_string's
1249         for var in variables_used_hash.keys():
1250                 if var.type == "val_string8" or var.type == "val_string16":
1251                         print ""
1252                         print `var`
1253
1254
1255         print """
1256 void
1257 proto_register_ncp2222(void)
1258 {
1259
1260         static hf_register_info hf[] = {
1261         { &hf_ncp_func,
1262         { "Function", "ncp.func", FT_UINT8, BASE_HEX, NULL, 0x0, "" }},
1263
1264         { &hf_ncp_length,
1265         { "Packet Length", "ncp.length", FT_UINT16, BASE_DEC, NULL, 0x0, "" }},
1266
1267         { &hf_ncp_subfunc,
1268         { "SubFunction", "ncp.subfunc", FT_UINT8, BASE_HEX, NULL, 0x0, "" }},
1269
1270         { &hf_ncp_completion_code,
1271         { "Completion Code", "ncp.completion_code", FT_UINT8, BASE_HEX, NULL, 0x0, "" }},
1272
1273         { &hf_ncp_connection_status,
1274         { "Connection Status", "ncp.connection_status", FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1275         """
1276
1277         # Print the registration code for the hf variables
1278         for var in variables_used_hash.keys():
1279                 print "\t{ &%s," % (var.HFName())
1280                 print "\t{ \"%s\", \"%s\", %s, %s, %s, 0x%x, \"\" }},\n" % \
1281                         (var.Description(), var.DFilter(),
1282                         var.EtherealFType(), var.Display(), var.ValuesName(),
1283                         var.Mask())
1284
1285         print """\t};
1286
1287                 proto_register_field_array(proto_ncp, hf, array_length(hf));
1288         }
1289         """
1290
1291
1292         # Determine which error codes are not used
1293         errors_not_used = {}
1294         # Copy the keys from the error list...
1295         for code in errors.keys():
1296                 errors_not_used[code] = 1
1297         # ... and remove the ones that *were* used.
1298         for code in errors_used_list:
1299                 del errors_not_used[code]
1300
1301         # Print a remark showing errors not used
1302         list_errors_not_used = errors_not_used.keys()
1303         list_errors_not_used.sort()
1304         for code in list_errors_not_used:
1305                 print "/* Error 0x%04x not used: %s */" % (code, errors[code])
1306         print "\n"
1307
1308         # Print the errors table
1309         print "/* Error strings. */"
1310         print "static const char *ncp_errors[] = {"
1311         for code in errors_used_list:
1312                 print '\t/* %02d (0x%04x) */ "%s",' % (errors_used_hash[code], code, errors[code])
1313         print "};\n"
1314
1315
1316
1317
1318         # Determine which groups are not used
1319         groups_not_used = {}
1320         # Copy the keys from the group list...
1321         for group in groups.keys():
1322                 groups_not_used[group] = 1
1323         # ... and remove the ones that *were* used.
1324         for group in groups_used_list:
1325                 del groups_not_used[group]
1326
1327         # Print a remark showing groups not used
1328         list_groups_not_used = groups_not_used.keys()
1329         list_groups_not_used.sort()
1330         for group in list_groups_not_used:
1331                 print "/* Group not used: %s = %s */" % (group, groups[group])
1332         print "\n"
1333
1334         # Print the groups table
1335         print "/* Group strings. */"
1336         print "static const char *ncp_groups[] = {"
1337         for group in groups_used_list:
1338                 print '\t/* %02d (%s) */ "%s",' % (groups_used_hash[group], group, groups[group])
1339         print "};\n"
1340
1341         # Print PTVC's
1342         print "/* PTVC records. These are re-used to save space. */"
1343         for ptvc in ptvc_lists.Members():
1344                 if not ptvc.Null() and not ptvc.Empty():
1345                         print "static const ptvc_record %s[] = {" % (ptvc.Name())
1346                         records = ptvc.Records()
1347                         for ptvc_rec in records:
1348                                 print "\t%s," % (ptvc_rec)
1349                         print "\t{ NULL, 0, 0 }"
1350                         print "};\n"
1351
1352         # Print error_equivalency tables
1353         print "/* Error-Equivalency Tables. These are re-used to save space. */"
1354         for compcodes in compcode_lists.Members():
1355                 errors = compcodes.Records()
1356                 # Make sure the record for error = 0x00 comes last.
1357                 print "static const error_equivalency %s[] = {" % (compcodes.Name())
1358                 for error in errors:
1359                         error_in_packet = error >> 8;
1360                         ncp_error_index = errors_used_hash[error]
1361                         print "\t{ 0x%02x, %d }, /* 0x%04x */" % (error_in_packet,
1362                                 ncp_error_index, error)
1363                 print "\t{ 0x00, -1 }\n};\n"
1364
1365
1366         # Print ncp_record packet records
1367         print "#define SUBFUNC 0xff"
1368         print "#define NOSUB   0x00"
1369
1370         print "/* ncp_record structs for packets */"
1371         print "static const ncp_record ncp_packets[] = {"
1372         for pkt in packets.Members():
1373                 if pkt.HasSubFunction():
1374                         subfunc_string = "SUBFUNC"
1375                 else:
1376                         subfunc_string = "NOSUB"
1377                 print '\t{ 0x%02x, 0x%02x, %s, "%s",' % (pkt.FunctionCode('high'),
1378                         pkt.FunctionCode('low'), subfunc_string, pkt.Description()),
1379
1380                 print '\t%d /* %s */,' % (groups_used_hash[pkt.Group()], pkt.Group())
1381
1382                 ptvc = pkt.PTVCRequest()
1383                 if not ptvc.Null() and not ptvc.Empty():
1384                         ptvc_request = ptvc.Name()
1385                 else:
1386                         ptvc_request = 'NULL'
1387
1388                 ptvc = pkt.PTVCReply()
1389                 if not ptvc.Null() and not ptvc.Empty():
1390                         ptvc_reply = ptvc.Name()
1391                 else:
1392                         ptvc_reply = 'NULL'
1393
1394                 errors = pkt.CompletionCodes()
1395                 print '\t\t%s, NULL, %s, NULL,' % (ptvc_request, ptvc_reply)
1396                 print '\t\t%s },\n' % (errors.Name())
1397
1398         print '\t{ 0, 0, 0, NULL }'
1399         print "};\n"
1400
1401         print "/* ncp funcs that require a subfunc */"
1402         print "static const guint8 ncp_func_requires_subfunc[] = {"
1403         hi_seen = {}
1404         for pkt in packets.Members():
1405                 if pkt.HasSubFunction():
1406                         hi_func = pkt.FunctionCode('high')
1407                         if not hi_seen.has_key(hi_func):
1408                                 print "\t0x%02x," % (hi_func)
1409                                 hi_seen[hi_func] = 1
1410         print "\t0"
1411         print "};\n"
1412
1413
1414         print '#include "ncp2222.h"'
1415
1416