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")
9 Data comes from "Programmer's Guide to the NetWare Core Protocol"
10 by Steve Conner and Dianne Conner.
12 Novell provides info at:
14 http://developer.novell.com/ndk (where you can download an *.exe file which
19 http://developer.novell.com/ndk/doc/docui/index.htm#../ncp/ncp__enu/data/
20 for a badly-formatted HTML version of the same PDF.
23 $Id: ncp2222.py,v 1.10 2001/06/28 02:42:48 gram Exp $
25 Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
27 This program is free software; you can redistribute it and/or
28 modify it under the terms of the GNU General Public License
29 as published by the Free Software Foundation; either version 2
30 of the License, or (at your option) any later version.
32 This program is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with this program; if not, write to the Free Software
39 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
51 ##############################################################################
53 ##############################################################################
55 class UniqueCollection:
56 """The UniqueCollection class stores objects which can be compared to other
57 objects of the same class. If two objects in the collection are equivalent,
58 only one is stored."""
60 def __init__(self, name):
65 def Add(self, object):
66 """Add an object to the members lists, if a comparable object
67 doesn't already exist. The object that is in the member list, that is
68 either the object that was added or the comparable object that was
69 already in the member list, is returned."""
71 # Is 'object' a duplicate of some other member?
72 for member in self.members:
76 # Store object in our members list.
77 self.members.append(object)
81 "Returns the list of members."
84 def HasMember(self, object):
85 "Does the list of members contain the object?"
86 for member in self.members:
94 ##############################################################################
97 "NamedList's keep track of PTVC's and Completion Codes"
98 def __init__(self, name, list):
103 def __cmp__(self, other):
104 "Compare this NamedList to another"
106 # Python will do a deep comparison of lists within lists.
107 if self.list < other.list:
109 elif self.list > other.list:
115 "String representation"
116 return "NamedList: " + `self.list`
118 def Name(self, new_name = None):
119 "Get/Set name of list"
125 "Returns record lists"
129 "Is there no list (different from an empty list)?"
130 return self.list == None
133 "It the list empty (different from a null list)?"
134 assert(not self.Null())
142 class PTVC(NamedList):
143 """ProtoTree TVBuff Cursor List ("PTVC List") Class"""
145 def __init__(self, name, records):
148 NamedList.__init__(self, name, self.list)
150 expected_offset = None
152 # Make a PTVCRecord object for each list in 'records'
153 for record in records:
154 ptvc_rec = PTVCRecord(record)
156 if expected_offset == None:
157 expected_offset = ptvc_rec.Offset()
159 elif expected_offset == -1:
162 elif expected_offset != ptvc_rec.Offset():
163 sys.stderr.write("Expected offset in %s to be %d\n" % (name,
167 # We can't make a PTVC list from a variable-length
168 # packet, unless it's FT_UINT_STRING
169 if type(ptvc_rec.Length()) == type(()):
170 if isinstance(ptvc_rec.Field(), nstring8):
177 elif expected_offset > -1:
178 expected_offset = expected_offset + ptvc_rec.Length()
181 self.list.append(ptvc_rec)
184 def __init__(self, record):
186 self.offset = record[0]
187 self.length = record[1]
188 self.field = record[2]
191 field_length = self.field.Length()
193 # if type(field_length) != type(self.length):
194 # sys.stderr.write("Length types do not match")
197 # if type(field_length) == type(0) and field_length > 0:
198 # if field_length != self.length:
199 # sys.stderr.write("Length %d does not match field length %d for field %s\n" % (self.length, field_length, self.field.Abbreviation()))
202 # Check if an endianness override is given
204 self.endianness = record[3]
206 # If no endianness was given in the record, then
207 # use the field's default endianness.
209 self.endianness = self.field.Endianness()
211 def __cmp__(self, other):
212 "Comparison operator"
213 if self.length < other.length:
215 elif self.length > other.length:
218 if self.field != other.field:
220 elif self.endianness != other.endianness:
226 "String representation"
228 if self.endianness == LE:
233 if type(self.length) == type(0):
236 var_length = self.field.Length()
241 return "{ &%s, %d, %s }" % (self.field.HFName(),
244 length = "PTVC_VARIABLE_LENGTH"
245 return "{ &%s, %s, %s }" % (self.field.HFName(),
258 ##############################################################################
262 def __init__(self, func_code, description, group, has_length=1):
264 self.func_code = func_code
265 self.description = description
268 self.request_records = None
269 self.reply_records = None
270 self.has_length = has_length
272 if not groups.has_key(group):
273 sys.stderr.write("NCP 0x%x has invalid group '%s'\n" % (self.func_code, group))
276 if self.HasSubFunction():
277 # NCP Function with SubFunction
278 self.start_offset = 10
280 # Simple NCP Function
281 self.start_offset = 7
283 def FunctionCode(self, part=None):
284 "Returns the function code for this NCP packet."
286 return self.func_code
288 if self.HasSubFunction():
289 return (self.func_code & 0xff00) >> 8
291 return self.func_code
293 if self.HasSubFunction():
294 return self.func_code & 0x00ff
298 sys.stderr.write("Unknown directive '%s' for function_code()\n" % (part))
301 def HasSubFunction(self):
302 "Does this NPC packet require a subfunction field?"
303 if self.func_code <= 0xff:
309 return self.has_length
311 def Description(self):
312 return self.description
317 def PTVCRequest(self):
318 return self.ptvc_request
321 return self.ptvc_reply
323 def Request(self, size, records=[]):
324 self.request_size = size
325 self.request_records = records
326 if self.HasSubFunction():
328 self.CheckRecords(size, records, "Request", 10)
330 self.CheckRecords(size, records, "Request", 8)
332 self.CheckRecords(size, records, "Request", 7)
333 self.ptvc_request = self.MakePTVC(records, "request")
335 def Reply(self, size, records=[]):
336 self.reply_size = size
337 self.reply_records = records
338 self.CheckRecords(size, records, "Reply", 8)
339 self.ptvc_reply = self.MakePTVC(records, "reply")
341 def CheckRecords(self, size, records, descr, min_hdr_length):
342 "Simple sanity check"
345 if type(size) == type(()):
349 lower = min_hdr_length
350 upper = min_hdr_length
352 for record in records:
356 if type(rec_size) == type(()):
357 rec_lower = rec_size[0]
358 rec_upper = rec_size[1]
360 lower = lower + rec_lower
361 upper = upper + rec_upper
365 sys.stderr.write("%s records for 2222/0x%x sum to %d bytes minimum, but param1 shows %d\n" \
366 % (descr, self.FunctionCode(), lower, min))
369 sys.stderr.write("%s records for 2222/0x%x sum to %d bytes maximum, but param1 shows %d\n" \
370 % (descr, self.FunctionCode(), upper, max))
377 def MakePTVC(self, records, name_suffix):
378 """Makes a PTVC out of a request or reply record list. Possibly adds
379 it to the global list of PTVCs (the global list is a UniqueCollection,
380 so an equivalent PTVC may already be in the global list)."""
382 name = "%s_%s" % (self.CName(), name_suffix)
383 ptvc = PTVC(name, records)
384 return ptvc_lists.Add(ptvc)
387 "Returns a C symbol based on the NCP function code"
388 return "ncp_0x%x" % (self.func_code)
391 """Returns a list of variables used in the request and reply records.
392 A variable is listed only once, even if it is used twice (once in
393 the request, once in the reply)."""
396 if self.request_records:
397 for record in self.request_records:
401 if self.reply_records:
402 for record in self.reply_records:
406 return variables.keys()
409 def CompletionCodes(self, codes=None):
410 """Sets or returns the list of completion codes. Internally, a NamedList
411 is used to store the completion codes, but the caller of this function
412 never realizes that because Python lists are the input and output."""
420 if not errors.has_key(code):
421 sys.stderr.write("Errors table does not have key 0x%04x for NCP=0x%x\n" % (code,
425 # Delay the exit until here so that the programmer can get the complete
426 # list of missing error codes
430 # Create CompletionCode (NamedList) object and possible add it to
431 # the global list of completion code lists.
432 name = "%s_errors" % (self.CName())
434 codes_list = NamedList(name, codes)
435 self.codes = compcode_lists.Add(codes_list)
440 """Adds the NCP object to the global collection of NCP objects. This
441 is done automatically after setting the CompletionCode list. Yes, this
442 is a shortcut, but it makes our list of NCP packet definitions look
443 neater, since an explicit "add to global list of packets" is not needed."""
445 # Add packet to global collection of packets
446 if packets.HasMember(self):
447 sys.stderr.write("Already have NCP Function Code 0x%x\n" % \
455 ##############################################################################
457 LE = 1 # Little-Endian
459 NA = -1 # Not Applicable
462 " Virtual class for NCP field types"
469 def __init__(self, abbrev, descr, bytes, endianness = NA):
477 def Abbreviation(self):
480 def Description(self):
484 return "hf_ncp_" + self.abbrev
487 return "ncp." + self.abbrev
489 def EtherealFType(self):
492 def Display(self, newval=None):
497 def ValuesName(self):
503 def Endianness(self):
504 return self.endianness
509 def __init__(self, abbrev, descr):
510 Type.__init__(self, abbrev, descr, 1)
512 # Same as above. Both are provided for convenience
516 def __init__(self, abbrev, descr):
517 Type.__init__(self, abbrev, descr, 1)
522 def __init__(self, abbrev, descr, endianness = BE):
523 Type.__init__(self, abbrev, descr, 2, endianness)
528 def __init__(self, abbrev, descr, endianness = BE):
529 Type.__init__(self, abbrev, descr, 4, endianness)
531 class nstring8(Type):
532 """A string of up to 255 characters. The first byte
533 gives the string length. Thus, the total length of
534 this data structure is from 1 to 256 bytes, including
538 ftype = "FT_UINT_STRING"
539 def __init__(self, abbrev, descr):
540 Type.__init__(self, abbrev, descr, 1)
542 class fw_string(Type):
543 """A fixed-width string of n bytes."""
548 def __init__(self, abbrev, descr, bytes):
549 Type.__init__(self, abbrev, descr, bytes)
553 "NUL-terminated string, with a maximum length"
557 def __init__(self, abbrev, descr):
558 Type.__init__(self, abbrev, descr, -1)
560 class val_string(Type):
561 """Abstract class for val_stringN, where N is number
562 of bits that key takes up."""
567 def __init__(self, abbrev, descr, val_string_array, endianness = BE):
568 Type.__init__(self, abbrev, descr, self.bytes, endianness)
569 self.values = val_string_array
572 result = "static const value_string %s[] = {\n" \
573 % (self.ValuesCName())
574 for val_record in self.values:
575 value = val_record[0]
577 value_repr = self.value_format % value
578 result = result + '\t{ %s,\t"%s" },\n' \
581 value_repr = self.value_format % 0
582 result = result + "\t{ %s,\tNULL },\n" % (value_repr)
583 result = result + "};\n"
587 def ValuesCName(self):
588 return "ncp_%s_vals" % (self.abbrev)
590 def ValuesName(self):
591 return "VALS(%s)" % (self.ValuesCName())
593 class val_string8(val_string):
597 value_format = "0x%02x"
599 class val_string16(val_string):
600 type = "val_string16"
603 value_format = "0x%04x"
609 def __init__(self, abbrev, descr, bytes):
610 Type.__init__(self, abbrev, descr, bytes, NA)
615 # def __init__(self, abbrev, descr):
616 # Type.__init__(self, abbrev, descr, -1)
618 # def length_var(self, length_var):
619 # self.length_var = length_var
621 ##############################################################################
622 # NCP Field Types. Defined in Appendix A of "Programmer's Guide..."
623 ##############################################################################
624 AcceptedMaxSize = uint16("accepted_max_size", "Accepted Max Size")
625 AcctVersion = byte("acct_version", "Acct Version")
626 BufferSize = uint16("buffer_size", "Buffer Size")
627 ConnectionNumber = uint32("connection_number", "Connection Number")
628 ConnectionsSupportedMax = uint16("connections_supported_max", "Connections Supported Max")
629 ConnectionsInUse = uint16("connections_in_use", "Connections In Use")
630 ConnectionsMaxUsed = uint16("connections_max_used", "Connections Max Used")
631 DirHandle = byte("dir_handle", "Directory Handle")
633 EchoSocket = uint16("echo_socket", "Echo Socket")
634 EchoSocket.Display('BASE_HEX')
636 FileHandle = bytes("file_handle", "File Handle", 6)
638 FileLock = val_string8("file_lock", "File Lock", [
639 [ 0x00, "Not Locked" ],
640 [ 0xfe, "Locked by file lock" ],
644 FileOffset = uint32("file_offset", "File Offset")
645 FilePath = nstring8("file_path", "File Path")
646 FileSize = uint32("file_size", "File Size")
647 InternetBridgeVersion = byte("internet_bridge_version", "Internet Bridge Version")
648 JobType = uint16("job_type", "Job Type")
650 LocalLoginInfoCcode = byte("local_login_info_ccode", "Local Login Info C Code")
651 LogicalLockType = val_string8("logical_lock_type", "Logical Lock Type", [
652 [ 0x00, "Log file" ],
653 [ 0x01, "Log and lock file for exclusive read/write use" ],
654 [ 0x03, "Log and lock with shareable read-only use" ],
657 LogicalRecordName = nstring8("logical_record_name", "Logical Record Name")
658 LogLockType = byte("log_lock_type", "Log Lock Type")
660 MaxBytes = uint16("max_bytes", "Maximum Number of Bytes")
661 MixedModePathFlag = byte("mixed_mode_path_flag", "Mixed Mode Path Flag")
662 NumBytes = uint16("num_bytes", "Number of Bytes")
664 ObjectFlags = val_string8("object_flags", "Object Flags", [
665 [ 0x00, "Dynamic object" ],
666 [ 0x01, "Static object" ],
669 ObjectHasProperties = val_string8("object_has_properites", "Object Has Properties", [
670 [ 0x00, "No properties" ],
671 [ 0xff, "One or more properties" ],
674 ObjectID = uint32("object_id", "Object ID")
675 ObjectID.Display('BASE_HEX')
677 ObjectName = nstring8("object_name", "Object Name")
678 ObjectName1 = fw_string("object_name1", "Object Name", 48)
680 ObjectSecurity = val_string8("object_security", "Object Security", [
681 [ 0x00, "Anyone can read or modify the object" ],
682 [ 0x01, "Client logged into the file server can read the object" ],
683 [ 0x02, "Client logged into the file server with the object's name, type and password can read the object" ],
684 [ 0x03, "Client with supervisor equivalence can read the object" ],
685 [ 0x04, "Only the operating system can read the object" ],
686 [ 0x10, "Client logged into the file server can modify the object" ],
687 [ 0x20, "Client logged into the file server with the object's name, type and password can modify the object" ],
688 [ 0x30, "Client with supervisor equivalence can modify the object" ],
689 [ 0x40, "Only the operating system can modify the object" ],
692 ObjectType = val_string16("object_type", "Object Type", [
693 [ 0x0000, "Unknown" ],
695 [ 0x0002, "User group" ],
696 [ 0x0003, "Print queue" ],
697 [ 0x0004, "NetWare file server" ],
698 [ 0x0005, "Job server" ],
699 [ 0x0006, "Gateway" ],
700 [ 0x0007, "Print server" ],
701 [ 0x0008, "Archive queue" ],
702 [ 0x0009, "Archive server" ],
703 [ 0x000a, "Job queue" ],
704 [ 0x000b, "Administration" ],
705 [ 0x0021, "NAS SNA gateway" ],
706 [ 0x0026, "Remote bridge server" ],
707 [ 0x0027, "TCP/IP gateway" ],
710 OSLanguageID = byte("os_language_id", "OS Language ID")
711 OSMajorVersion = byte("os_major_version", "OS Major Version")
712 OSMinorVersion = byte("os_minor_version", "OS Minor Version")
713 OSRevision = byte("os_revision", "OS Revision")
714 PingVersion = uint16("ping_version", "Ping Version", endianness=LE)
715 PingVersion.Display("BASE_HEX")
717 PrintServerVersion = byte("print_server_version", "Print Server Version")
718 ProductMajorVersion = uint16("product_major_version", "Product Major Version")
719 ProductMinorVersion = uint16("product_minor_version", "Product Minor Version")
720 ProductRevisionVersion = byte("product_revision_version", "Product Revision Version")
722 PropertyHasMoreSegments = val_string8("property_has_more_segments",
723 "Property Has More Segments", [
724 [ 0x00, "Is last segment" ],
725 [ 0xff, "More segments are available" ],
728 PropertyName = nstring8("property_name", "Property Name")
729 PropertyData = bytes("property_data", "Property Data", 128)
730 PropertySegment = uint8("property_segment", "Property Segment")
732 PropertyType = val_string8("property_type", "Property Type", [
733 [ 0x00, "Static item" ],
734 [ 0x01, "Dynamic item" ],
735 [ 0x02, "Static set" ],
736 [ 0x03, "Dynamic set" ],
739 ProposedMaxSize = uint16("proposed_max_size", "Proposed Max Size")
741 Reserved3 = bytes("reserved3", "Reserved", 3)
742 Reserved51 = bytes("reserved51", "Reserved", 51)
744 QMSVersion = byte("qms_version", "QMS Version")
746 # XXX - needs bitfield
747 SecurityFlag = byte("security_flag", "Security Flag")
748 SecurityRestrictionVersion = byte("security_restriction_version", "Security Restriction Version")
750 ServerName = stringz("server_name", "Server Name")
751 SFTLevel = byte("sft_level", "SFT Level")
753 TaskNumber = uint32("task_number", "Task Number")
754 TimeoutLimit = uint16("timeout_limit", "Timeout Limit")
755 TTSLevel = byte("tts_level", "TTS Level")
756 UnknownByte = byte("unknown_byte", "Unknown Byte")
758 VAPVersion = byte("vap_version", "VAP Version")
759 VirtualConsoleVersion = byte("virtual_console_version", "Virtual Console Version")
760 VolumesSupportedMax = uint16("volumes_supported_max", "Volumes Supported Max")
762 ##############################################################################
764 ##############################################################################
766 groups['accounting'] = "Accounting"
767 groups['afp'] = "AFP"
768 groups['auditing'] = "Auditing"
769 groups['bindery'] = "Bindery"
770 groups['connection'] = "Connection"
771 groups['directory'] = "Directory"
772 groups['extended'] = "Extended Attribute"
773 groups['file'] = "File"
774 groups['fileserver'] = "File Server"
775 groups['message'] = "Message"
776 groups['migration'] = "Data Migration"
777 groups['misc'] = "Miscellaneous"
778 groups['name'] = "Name Space"
779 groups['nds'] = "NetWare Directory"
780 groups['print'] = "Print"
781 groups['queue'] = "Queue"
782 groups['sync'] = "Synchronization"
783 groups['tss'] = "Transaction Tracking"
784 groups['unknown'] = "Unknown"
786 ##############################################################################
788 ##############################################################################
790 errors[0x0000] = "Ok"
791 errors[0x0001] = "Transaction tracking is available"
792 errors[0x0002] = "Ok. The data has been written"
794 errors[0x0100] = "One or more of the ConnectionNumbers in the send list are invalid"
795 errors[0x0101] = "Invalid space limit"
796 errors[0x0102] = "Insufficient disk space"
797 errors[0x0103] = "Queue server cannot add jobs"
798 errors[0x0104] = "Out of disk space"
799 errors[0x0105] = "Semaphore overflow"
801 errors[0x0200] = "One or more clients in the send list are not logged in"
802 errors[0x0201] = "Queue server cannot attach"
804 errors[0x0300] = "One or more clients in the send list are not accepting messages"
806 errors[0x0400] = "Client already has message"
807 errors[0x0401] = "Queue server cannot service job"
809 errors[0x7e00] = "NCP failed boundary check"
811 errors[0x8000] = "Lock fail"
812 errors[0x8100] = "A file handle could not be allocated by the file server"
813 errors[0x8200] = "Unauthorized to open the file"
814 errors[0x8300] = "Unable to read/write the volume. Possible bad sector on the file server"
816 errors[0x8400] = "Unauthorized to create the directory"
817 errors[0x8401] = "Unauthorized to create the file"
819 errors[0x8500] = "Unauthorized to delete the specified file"
820 errors[0x8501] = "Unauthorized to overwrite an existing file in this directory"
822 errors[0x8700] = "An unexpected character was encountered in the filename"
823 errors[0x8800] = "Invalid file handle"
824 errors[0x8900] = "Unauthorized to search this directory"
825 errors[0x8a00] = "Unauthorized to delete this directory"
826 errors[0x8b00] = "Unauthorized to rename a file in this directory"
828 errors[0x8c00] = "No set privileges"
829 errors[0x8c01] = "Unauthorized to modify a file in this directory"
830 errors[0x8c02] = "Unauthorized to change the restriction on this volume"
832 errors[0x8d00] = "Some of the affected files are in use by another client"
833 errors[0x8d01] = "The affected file is in use"
835 errors[0x8e00] = "All of the affected files are in use by another client"
836 errors[0x8f00] = "Some of the affected files are read-only"
838 errors[0x9000] = "An attempt to modify a read-only volume occurred"
839 errors[0x9001] = "All of the affected files are read-only"
841 errors[0x9100] = "Some of the affected files already exist"
843 errors[0x9200] = "Directory with the new name already exists"
844 errors[0x9201] = "All of the affected files already exist"
846 errors[0x9300] = "Unauthorized to read from this file"
847 errors[0x9400] = "Unauthorized to write to this file"
848 errors[0x9500] = "The affected file is detached"
850 errors[0x9600] = "The file server has run out of memory to service this request"
851 errors[0x9601] = "No alloc space for message"
853 errors[0x9800] = "The affected volume is not mounted"
854 errors[0x9801] = "The volume associated with VolumeNumber is not mounted"
855 errors[0x9802] = "The resulting voume does not exist"
856 errors[0x9803] = "The destination volume is not mounted"
858 errors[0x9900] = "The file server has run out of directory space on the affected volume"
859 errors[0x9a00] = "The request attempted to rename the affected file to another volume"
861 errors[0x9b00] = "DirHandle is not associated with a valid directory path"
862 errors[0x9b01] = "A resulting directory handle is not associated with a valid directory path"
863 errors[0x9b02] = "The directory associated with DirHandle does not exist"
864 errors[0x9b03] = "Bad directory handle"
866 errors[0x9c00] = "The resulting path is not valid"
867 errors[0x9c01] = "The resulting file path is not valid"
868 errors[0x9c02] = "The resulting directory path is not valid"
869 errors[0x9c03] = "Invalid path"
871 errors[0x9d00] = "A directory handle was not available for allocation"
873 errors[0x9e00] = "The name of the directory does not conform to a legal name for this name space"
874 errors[0x9e01] = "The new directory name does not conform to a legal name for this name space"
876 errors[0x9f00] = "The request attempted to delete a directory that is in use by another client"
878 errors[0xa000] = "The request attempted to delete a directory that is not empty"
879 errors[0xa100] = "An unrecoverable error occured on the affected directory"
880 errors[0xa200] = "The request attempted to read from a file region that is physically locked"
881 errors[0xa400] = "Invalid directory rename attempted"
883 errors[0xbf00] = "Requests for this name space are not valid on this volume"
885 errors[0xc000] = "Unauthorized to retrieve accounting data"
886 errors[0xc100] = "The ACCOUNT_BALANCE property does not exist"
887 errors[0xc200] = "The object has exceeded its credit limit"
888 errors[0xc300] = "Too many holds have been placed against this account"
889 errors[0xc400] = "The client account has been disabled"
891 errors[0xc500] = "Access to the account has been denied because of intruder detection"
892 errors[0xc501] = "Login lockout"
894 errors[0xc600] = "The caller does not have operator priviliges"
895 errors[0xc601] = "The client does not have operator priviliges"
897 errors[0xd000] = "Queue error"
898 errors[0xd100] = "The queue does not exist"
900 errors[0xd200] = "A queue server is not associated with this queue"
901 errors[0xd201] = "A queue server is not associated with the selected queue"
902 errors[0xd202] = "No queue server"
904 errors[0xd300] = "No queue rights"
906 errors[0xd400] = "The queue is full and cannot accept another request"
907 errors[0xd401] = "The queue associated with ObjectId is full and cannot accept another request"
909 errors[0xd500] = "A job does not exist in this queue"
910 errors[0xd501] = "No queue job"
911 errors[0xd502] = "The job associated with JobNumber does not exist in this queue"
913 errors[0xd600] = "The file server does not allow unencrypted passwords"
914 errors[0xd601] = "No job right"
916 errors[0xd700] = "Bad account"
917 errors[0xd701] = "The old and new password strings are identical"
918 errors[0xd702] = "The job is currently being serviced"
919 errors[0xd703] = "The queue is currently servicing a job"
920 errors[0xd704] = "Queue servicing"
922 errors[0xd800] = "Queue not active"
924 errors[0xd900] = "The file server cannot accept another connection as it has reached its limit"
925 errors[0xd901] = "The client is not security equivalent to one of the objects in the Q_SERVERS group property of the target queue"
926 errors[0xd902] = "Station is not a server"
928 errors[0xda00] = "Attempted to login to the file server during a restricted time period"
929 errors[0xda01] = "Queue halted"
931 errors[0xdb00] = "Attempted to login to the file server from an unauthorized workstation or network"
932 errors[0xdb01] = "The queue cannot attach another queue server"
933 errors[0xdb02] = "Maximum queue servers"
935 errors[0xde00] = "Attempted to login to the file server with an incorrect password"
936 errors[0xdf00] = "Attempted to login to the file server with a password that has expired"
938 errors[0xe700] = "No disk track"
939 errors[0xe800] = "Write to group"
940 errors[0xe900] = "The object is already a member of the group property"
942 errors[0xea00] = "No such member"
943 errors[0xea01] = "The bindery object is not a member of the set"
944 errors[0xea02] = "Non-existent member"
946 errors[0xeb00] = "The property is not a set property"
948 errors[0xec00] = "No such set"
949 errors[0xec01] = "The set property does not exist"
951 errors[0xed00] = "Property exists"
952 errors[0xed01] = "The property already exists"
953 errors[0xed02] = "An attempt was made to create a bindery object property that already exists"
955 errors[0xee00] = "The object already exists"
956 errors[0xee01] = "The bindery object already exists"
958 errors[0xef00] = "Illegal name"
959 errors[0xef01] = "Illegal characters in ObjectName field"
960 errors[0xef02] = "Invalid name"
962 errors[0xf000] = "A wildcard was detected in a field that does not support wildcards"
963 errors[0xf001] = "An illegal wildcard was detected in ObjectName"
965 errors[0xf100] = "The client does not have the rights to access this bindery object"
966 errors[0xf101] = "Bindery security"
967 errors[0xf102] = "Invalid bindery security"
969 errors[0xf200] = "Unauthorized to read from this object"
970 errors[0xf300] = "Unauthorized to rename this object"
972 errors[0xf400] = "Unauthorized to delete this object"
973 errors[0xf401] = "No object delete privileges"
974 errors[0xf402] = "Unauthorized to delete this queue"
976 errors[0xf500] = "Unauthorized to create this object"
977 errors[0xf501] = "No object create"
979 errors[0xf600] = "No property delete"
980 errors[0xf601] = "Unauthorized to delete the property of this object"
981 errors[0xf602] = "Unauthorized to delete this property"
983 errors[0xf700] = "Unauthorized to create this property"
984 errors[0xf701] = "No property create privilege"
986 errors[0xf800] = "Unauthorized to write to this property"
987 errors[0xf900] = "Unauthorized to read this property"
988 errors[0xfa00] = "Temporary remap error"
990 errors[0xfb00] = "No such property"
991 errors[0xfb01] = "The file server does not support this request"
992 errors[0xfb02] = "The specified property does not exist"
993 errors[0xfb03] = "The PASSWORD property does not exist for this bindery object"
994 errors[0xfb04] = "NDS NCP not available"
996 errors[0xfc00] = "The message queue cannot accept another message"
997 errors[0xfc01] = "The trustee associated with ObjectId does not exist"
998 errors[0xfc02] = "The specified bindery object does not exist"
999 errors[0xfc03] = "The bindery object associated with ObjectID does not exist"
1000 errors[0xfc04] = "A bindery object does not exist that matches"
1001 errors[0xfc05] = "The specified queue does not exist"
1002 errors[0xfc06] = "No such object"
1003 errors[0xfc07] = "The queue associated with ObjectID does not exist"
1005 errors[0xfd00] = "Bad station number"
1006 errors[0xfd01] = "The connection associated with ConnectionNumber is not active"
1007 errors[0xfd02] = "Lock collision"
1008 errors[0xfd03] = "Transacktion tracking is disabled"
1010 errors[0xfe00] = "I/O failure"
1011 errors[0xfe01] = "The files containing the bindery on the file server are locked"
1012 errors[0xfe02] = "A file with the specified name already exists in this directory"
1013 errors[0xfe03] = "No more restrictions were found"
1014 errors[0xfe04] = "The file server was unable to lock the file within the specified time limit"
1015 errors[0xfe05] = "The file server was unable to lock all files within the specified time limit"
1016 errors[0xfe06] = "The bindery object associated with ObjectID is not a valid trustee"
1017 errors[0xfe07] = "Directory locked"
1018 errors[0xfe08] = "Bindery locked"
1019 errors[0xfe09] = "Invalid semaphore name length"
1020 errors[0xfe0a] = "The file server was unable to complete the operation within the specified time limit"
1021 errors[0xfe0b] = "Transaction restart"
1022 errors[0xfe0c] = "Bad packet"
1024 errors[0xff00] = "Failure"
1025 errors[0xff01] = "Lock error"
1026 errors[0xff02] = "File not found"
1027 errors[0xff03] = "The file not found or cannot be unlocked"
1028 errors[0xff04] = "Record not found"
1029 errors[0xff05] = "The logical record was not found"
1030 errors[0xff06] = "The printer associated with PrinterNumber does not exist"
1031 errors[0xff07] = "No such printer"
1032 errors[0xff08] = "Unable to complete the request"
1033 errors[0xff09] = "Unauthorized to change privileges of this trustee"
1034 errors[0xff0a] = "No files matching the search criteria were found"
1035 errors[0xff0b] = "A file matching the search criteria was not found"
1036 errors[0xff0c] = "Verification failed"
1037 errors[0xff0d] = "Object associated with ObjectID is not a manager"
1038 errors[0xff0e] = "Invalid initial semaphore value"
1039 errors[0xff0f] = "The semaphore handle is not valid"
1040 errors[0xff10] = "SemaphoreHandle is not associated with a valid sempahore"
1041 errors[0xff11] = "Invalid semaphore handle"
1042 errors[0xff12] = "Transaction tracking is not available"
1043 errors[0xff13] = "The transaction has not yet been written to disk"
1044 errors[0xff14] = "Directory already exists"
1045 errors[0xff15] = "The file already exists and the deletion flag was not set"
1046 errors[0xff16] = "No matching files or directories were found"
1047 errors[0xff17] = "A file or directory matching the search criteria was not found"
1048 errors[0xff18] = "The file already exists"
1049 errors[0xff19] = "No files found"
1052 ##############################################################################
1054 ##############################################################################
1060 print " * Generated automatically from %s" % (sys.argv[0])
1061 print " * Do not edit this file manually, as all changes will be lost."
1066 * This program is free software; you can redistribute it and/or
1067 * modify it under the terms of the GNU General Public License
1068 * as published by the Free Software Foundation; either version 2
1069 * of the License, or (at your option) any later version.
1071 * This program is distributed in the hope that it will be useful,
1072 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1073 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1074 * GNU General Public License for more details.
1076 * You should have received a copy of the GNU General Public License
1077 * along with this program; if not, write to the Free Software
1078 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1081 #ifdef HAVE_CONFIG_H
1082 # include "config.h"
1087 #include "conversation.h"
1088 #include "ptvcursor.h"
1089 #include "packet-ncp-int.h"
1091 static int hf_ncp_func = -1;
1092 static int hf_ncp_length = -1;
1093 static int hf_ncp_subfunc = -1;
1094 static int hf_ncp_completion_code = -1;
1095 static int hf_ncp_connection_status = -1;
1098 # Look at all packet types in the packets collection, and cull information
1101 for packet in packets.Members():
1102 packet_keys.append(packet.FunctionCode())
1105 errors_used_list = []
1106 errors_used_hash = {}
1107 groups_used_list = []
1108 groups_used_hash = {}
1109 variables_used_hash = {}
1111 for pkt in packets.Members():
1112 # Determine which error codes are used.
1113 codes = pkt.CompletionCodes()
1114 for code in codes.Records():
1115 if not errors_used_hash.has_key(code):
1116 errors_used_hash[code] = len(errors_used_list)
1117 errors_used_list.append(code)
1119 # Determine which groups are used.
1121 if not groups_used_hash.has_key(group):
1122 groups_used_hash[group] = len(groups_used_list)
1123 groups_used_list.append(group)
1125 # Determine which variables are used.
1126 vars = pkt.Variables()
1128 variables_used_hash[var] = 1
1132 # Print the hf variable declarations
1133 for var in variables_used_hash.keys():
1134 print "static int " + var.HFName() + " = -1;"
1137 # Print the value_string's
1138 for var in variables_used_hash.keys():
1139 if var.type == "val_string8" or var.type == "val_string16":
1146 proto_register_ncp2222(void)
1149 static hf_register_info hf[] = {
1151 { "Function", "ncp.func", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1154 { "Packet Length", "ncp.length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }},
1157 { "SubFunction", "ncp.subfunc", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1159 { &hf_ncp_completion_code,
1160 { "Completion Code", "ncp.completion_code", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1162 { &hf_ncp_connection_status,
1163 { "Connection Status", "ncp.connection_status", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1166 # Print the registration code for the hf variables
1167 for var in variables_used_hash.keys():
1168 print "\t{ &%s," % (var.HFName())
1169 print "\t{ \"%s\", \"%s\", %s, %s, %s, 0x%x, \"\", HFILL }},\n" % \
1170 (var.Description(), var.DFilter(),
1171 var.EtherealFType(), var.Display(), var.ValuesName(),
1176 proto_register_field_array(proto_ncp, hf, array_length(hf));
1181 # Determine which error codes are not used
1182 errors_not_used = {}
1183 # Copy the keys from the error list...
1184 for code in errors.keys():
1185 errors_not_used[code] = 1
1186 # ... and remove the ones that *were* used.
1187 for code in errors_used_list:
1188 del errors_not_used[code]
1190 # Print a remark showing errors not used
1191 list_errors_not_used = errors_not_used.keys()
1192 list_errors_not_used.sort()
1193 for code in list_errors_not_used:
1194 print "/* Error 0x%04x not used: %s */" % (code, errors[code])
1197 # Print the errors table
1198 print "/* Error strings. */"
1199 print "static const char *ncp_errors[] = {"
1200 for code in errors_used_list:
1201 print '\t/* %02d (0x%04x) */ "%s",' % (errors_used_hash[code], code, errors[code])
1207 # Determine which groups are not used
1208 groups_not_used = {}
1209 # Copy the keys from the group list...
1210 for group in groups.keys():
1211 groups_not_used[group] = 1
1212 # ... and remove the ones that *were* used.
1213 for group in groups_used_list:
1214 del groups_not_used[group]
1216 # Print a remark showing groups not used
1217 list_groups_not_used = groups_not_used.keys()
1218 list_groups_not_used.sort()
1219 for group in list_groups_not_used:
1220 print "/* Group not used: %s = %s */" % (group, groups[group])
1223 # Print the groups table
1224 print "/* Group strings. */"
1225 print "static const char *ncp_groups[] = {"
1226 for group in groups_used_list:
1227 print '\t/* %02d (%s) */ "%s",' % (groups_used_hash[group], group, groups[group])
1230 # Print the group macros
1231 for group in groups_used_list:
1232 name = string.upper(group)
1233 print "#define NCP_GROUP_%s\t%d" % (name, groups_used_hash[group])
1237 print "/* PTVC records. These are re-used to save space. */"
1238 for ptvc in ptvc_lists.Members():
1239 if not ptvc.Null() and not ptvc.Empty():
1240 print "static const ptvc_record %s[] = {" % (ptvc.Name())
1241 records = ptvc.Records()
1242 for ptvc_rec in records:
1243 print "\t%s," % (ptvc_rec)
1244 print "\t{ NULL, 0, 0 }"
1247 # Print error_equivalency tables
1248 print "/* Error-Equivalency Tables. These are re-used to save space. */"
1249 for compcodes in compcode_lists.Members():
1250 errors = compcodes.Records()
1251 # Make sure the record for error = 0x00 comes last.
1252 print "static const error_equivalency %s[] = {" % (compcodes.Name())
1253 for error in errors:
1254 error_in_packet = error >> 8;
1255 ncp_error_index = errors_used_hash[error]
1256 print "\t{ 0x%02x, %d }, /* 0x%04x */" % (error_in_packet,
1257 ncp_error_index, error)
1258 print "\t{ 0x00, -1 }\n};\n"
1261 # Functions without length parameter
1262 funcs_without_length = {}
1265 # Print ncp_record packet records
1266 print "#define SUBFUNC_WITH_LENGTH 0x02"
1267 print "#define SUBFUNC_NO_LENGTH 0x01"
1268 print "#define NO_SUBFUNC 0x00"
1270 print "/* ncp_record structs for packets */"
1271 print "static const ncp_record ncp_packets[] = {"
1272 for pkt in packets.Members():
1273 if pkt.HasSubFunction():
1274 func = pkt.FunctionCode('high')
1276 subfunc_string = "SUBFUNC_WITH_LENGTH"
1277 # Ensure that the function either has a length param or not
1278 if funcs_without_length.has_key(func):
1279 sys.exit("Function 0x%04x sometimes has length param, sometimes not." \
1280 % (pkt.FunctionCode(),))
1282 subfunc_string = "SUBFUNC_NO_LENGTH"
1283 funcs_without_length[func] = 1
1285 subfunc_string = "NO_SUBFUNC"
1286 print '\t{ 0x%02x, 0x%02x, %s, "%s",' % (pkt.FunctionCode('high'),
1287 pkt.FunctionCode('low'), subfunc_string, pkt.Description()),
1289 print '\t%d /* %s */,' % (groups_used_hash[pkt.Group()], pkt.Group())
1291 ptvc = pkt.PTVCRequest()
1292 if not ptvc.Null() and not ptvc.Empty():
1293 ptvc_request = ptvc.Name()
1295 ptvc_request = 'NULL'
1297 ptvc = pkt.PTVCReply()
1298 if not ptvc.Null() and not ptvc.Empty():
1299 ptvc_reply = ptvc.Name()
1303 errors = pkt.CompletionCodes()
1304 print '\t\t%s, NULL, %s, NULL,' % (ptvc_request, ptvc_reply)
1305 print '\t\t%s },\n' % (errors.Name())
1307 print '\t{ 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL, NULL }'
1310 print "/* ncp funcs that require a subfunc */"
1311 print "static const guint8 ncp_func_requires_subfunc[] = {"
1313 for pkt in packets.Members():
1314 if pkt.HasSubFunction():
1315 hi_func = pkt.FunctionCode('high')
1316 if not hi_seen.has_key(hi_func):
1317 print "\t0x%02x," % (hi_func)
1318 hi_seen[hi_func] = 1
1323 print "/* ncp funs that have no length parameter */"
1324 print "static const guint8 ncp_func_has_no_length_parameter[] = {"
1325 funcs = funcs_without_length.keys()
1328 print "\t0x%02x," % (func,)
1333 print '#include "packet-ncp2222.inc"'
1338 global compcode_lists
1341 packets = UniqueCollection('NCP Packet Descriptions')
1342 compcode_lists = UniqueCollection('Completion Code Lists')
1343 ptvc_lists = UniqueCollection('PTVC Lists')
1351 def define_ncp2222():
1352 ##############################################################################
1353 # NCP Packets. Here I list functions and subfunctions in hexadecimal like the
1354 # NCP book (and I believe LanAlyzer does this too).
1355 # However, Novell lists these in decimal in their on-line documentation.
1356 ##############################################################################
1358 pkt = NCP(0x02, "File Release Lock", 'sync')
1361 pkt.CompletionCodes([0x0000, 0xff00])
1367 #pkt = NCP(0x03, "Log File", 'sync')
1368 #pkt.request( (12, 267), [
1369 # [ 7, 1, DirHandle ],
1370 # [ 8, 1, LogLockType ],
1371 # [ 9, 2, TimeoutLimit, LE ],
1372 # [ 11, (1, 256), FilePath ],
1374 #pkt.completion_codes([0x0000, 0x8200, 0x9600, 0xfe00, 0xff01])
1377 #pkt = NCP(0x04, "Lock File Set", 'sync')
1379 # [ 7, TimeoutLimit ],
1381 #pkt.completion_codes([0xfe, 0xff01])
1384 #pkt = NCP(0x05, "Release File", 'sync')
1389 #pkt.completion_codes([0x7e, 0x98, 0x9b, 0x9c, 0xff02])
1392 #pkt = NCP(0x06, "Release File Set", 'sync')
1394 # [ 7, UnknownByte ],
1396 #pkt.completion_codes()
1399 #pkt = NCP(0x07, "Clear File", 'sync')
1404 #pkt.completion_codes([0x7e, 0x96, 0x98, 0x9b, 0x9c,
1405 # 0xa1, 0xfd, 0xff])
1408 #pkt = NCP(0x08, "Clear File Set", 'sync')
1412 #pkt.completion_codes([0x7e])
1415 #pkt = NCP(0x09, "Log Logical Record", 'sync')
1417 # [ 7, LogicalLockType ],
1418 # [ 8, TimeoutLimit_be ],
1419 # [ 10, LogicalRecordName ],
1421 #pkt.completion_codes([0x96, 0xfe, 0xff])
1424 #pkt = NCP(0x0a, "Lock Logical Record Set", 'sync')
1426 # [ 7, LogicalLockType ],
1427 # [ 8, TimeoutLimit_le ],
1429 #pkt.completion_codes([0xfe, 0xff])
1432 #pkt = NCP(0x0b, "Clear Logical Record", 'sync')
1434 # [7, LogicalRecordName ],
1436 #pkt.completion_codes([0xff]
1444 #pkt = NCP(0x1100, "Lock Logical Record Set", 'sync')
1446 # [ 10, var_length_data("data").length_var("packetlength") ]
1448 #pkt.completion_codes()
1452 pkt = NCP(0x1711, "Get File Server Information", 'fileserver')
1455 [ 8, 48, ServerName ],
1456 [ 56, 1, OSMajorVersion ],
1457 [ 57, 1, OSMinorVersion ],
1458 [ 58, 2, ConnectionsSupportedMax ],
1459 [ 60, 2, ConnectionsInUse ],
1460 [ 62, 2, VolumesSupportedMax ],
1461 [ 64, 1, OSRevision ],
1462 [ 65, 1, SFTLevel ],
1463 [ 66, 1, TTSLevel ],
1464 [ 67, 2, ConnectionsMaxUsed ],
1465 [ 69, 1, AcctVersion ],
1466 [ 70, 1, VAPVersion ],
1467 [ 71, 1, QMSVersion ],
1468 [ 72, 1, PrintServerVersion ],
1469 [ 73, 1, VirtualConsoleVersion ],
1470 [ 74, 1, SecurityRestrictionVersion ],
1471 [ 75, 1, InternetBridgeVersion ],
1472 [ 76, 1, MixedModePathFlag ],
1473 [ 77, 1, LocalLoginInfoCcode ],
1474 [ 78, 2, ProductMajorVersion ],
1475 [ 80, 2, ProductMinorVersion ],
1476 [ 82, 2, ProductRevisionVersion ],
1477 [ 84, 1, OSLanguageID ],
1478 [ 85, 51, Reserved51 ],
1480 pkt.CompletionCodes([0x0000, 0x9600])
1484 pkt = NCP(0x1735, "Get Bindery Object ID", 'bindery')
1485 pkt.Request((13,60), [
1486 [ 10, 2, ObjectType ],
1487 [ 12, (1,48), ObjectName ],
1491 [ 12, 2, ObjectType ],
1492 [ 14, 48, ObjectName1 ],
1494 pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xf000, 0xfc02,
1498 pkt = NCP(0x1737, "Scan Bindery Object", 'bindery')
1499 pkt.Request((17,64), [
1500 [ 10, 4, ObjectID ],
1501 [ 14, 2, ObjectType ],
1502 [ 16, (1,48), ObjectName ],
1506 [ 12, 2, ObjectType ],
1507 [ 14, 48, ObjectName1 ],
1508 [ 62, 1, ObjectFlags ],
1509 [ 63, 1, ObjectSecurity ],
1510 [ 64, 1, ObjectHasProperties ],
1512 pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xfc02,
1516 pkt = NCP(0x173D, "Read Property Value", 'bindery')
1517 pkt.Request((15,77), [
1518 [ 10, 2, ObjectType ],
1519 [ 12, (1,48), ObjectName ],
1520 [ -1, 1, PropertySegment ],
1521 [ -1, (1,16), PropertyName ],
1524 [ 8, 128, PropertyData ],
1525 [ 136, 1, PropertyHasMoreSegments ],
1526 [ 137, 1, PropertyType ],
1528 pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0x9600, 0xec01,
1529 0xf000, 0xf100, 0xf900, 0xfb02, 0xfc02, 0xfe01, 0xff00 ])
1532 pkt = NCP(0x177C, "Service Queue Job", 'queue')
1534 [ 10, 4, ObjectID ],
1537 pkt.Reply(24, [ # XXX - 76, [
1538 [ 8, 4, ConnectionNumber ],
1539 [ 12, 4, TaskNumber ],
1540 [ 16, 4, ObjectID ],
1541 [ 20, 4, ObjectID ],
1544 # These completion codes are not documented, but guessed.
1545 pkt.CompletionCodes([0x0000, 0x9900, 0xd000, 0xd100, 0xd201, 0xd300,
1546 0xd401, 0xd502, 0xd601, 0xd704, 0xd800, 0xd901, 0xda01, 0xdb01,
1550 pkt = NCP(0x18, "End of Job", 'connection')
1553 pkt.CompletionCodes([0x0000])
1556 pkt = NCP(0x19, "Logout", 'connection')
1559 pkt.CompletionCodes([0x0000])
1562 pkt = NCP(0x21, "Negotiate Buffer Size", 'connection')
1564 [ 7, 2, BufferSize ],
1567 [ 8, 2, BufferSize ],
1569 pkt.CompletionCodes([0x0000])
1572 pkt = NCP(0x42, "Close File", 'file')
1574 [ 7, 6, FileHandle ],
1577 pkt.CompletionCodes([0x0000, 0xff00])
1580 pkt = NCP(0x47, "Get Current Size of File", 'file')
1582 [ 7, 6, FileHandle ],
1587 pkt.CompletionCodes([0x0000, 0x8800])
1590 pkt = NCP(0x48, "Read From A File", 'file')
1592 [ 7, 1, UnknownByte ],
1593 [ 8, 6, FileHandle ],
1594 [ 14, 4, FileOffset ], # my nomenclature
1595 [ 18, 2, MaxBytes ], # my nomenclature
1597 pkt.Reply(10, [ # XXX - (10,-1), [
1598 [ 8, 2, NumBytes ], # my nomenclature
1601 pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0xff00])
1603 # 2222/5701 - no info
1604 # 2222/5702 - no info
1605 # 2222/5706 - no info
1606 # 2222/5714 - no info
1610 pkt = NCP(0x61, "Get Big Packet NCP Max Packet Size", 'unknown')
1612 [ 7, 2, ProposedMaxSize ],
1613 [ 9, 1, SecurityFlag ],
1616 [ 8, 2, AcceptedMaxSize ],
1617 [ 10, 2, EchoSocket ],
1618 [ 12, 1, SecurityFlag ],
1620 pkt.CompletionCodes([0x0000])
1623 pkt = NCP(0x6801, "Ping for NDS NCP", "nds", has_length=0)
1627 # XXX - expand, Unicode
1629 [ 8, 2, PingVersion ],
1631 pkt.CompletionCodes([0x0000, 0xfb04, 0xfe0c])
1634 if __name__ == '__main__':