home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / software / pelne / optionp / ins.cab / rserver.vbs < prev    next >
Text File  |  1997-10-12  |  15KB  |  479 lines

  1. REM
  2. REM LOCALIZATION
  3. REM
  4.  
  5. L_SWITCH_OPERATION      = "-t"
  6. L_SWITCH_SERVER         = "-s"
  7. L_SWITCH_INSTANCE_ID    = "-v"
  8. L_SWITCH_CL_POSTS        = "-c"
  9. L_SWITCH_CL_SOFT        = "-l"
  10. L_SWITCH_CL_HARD        = "-h"
  11. L_SWITCH_FD_POSTS        = "-f"
  12. L_SWITCH_FD_SOFT        = "-i"
  13. L_SWITCH_FD_HARD        = "-j"
  14. L_SWITCH_SMTP_SERVER    = "-m"
  15. L_SWITCH_MODERATOR_DOMAIN    = "-d"
  16. L_SWITCH_UUCP_NAME        = "-u"
  17. L_SWITCH_CTRL_MSGS        = "-x"
  18. L_SWITCH_NNTPFILE        = "-n"
  19. L_SWITCH_ADMIN_EMAIL    = "-a"
  20. L_SWITCH_PICKUP            = "-p"
  21. L_SWITCH_FAILED_PICKUP    = "-q"
  22. L_SWITCH_DROP            = "-e"
  23. L_SWITCH_NEWNEWS        = "-b"
  24. L_SWITCH_HONOR_MSGIDS    = "-r"
  25. L_SWITCH_PORT           = "-o"
  26.  
  27. L_OP_GET            = "g"
  28. L_OP_SET            = "s"
  29. L_OP_CREATE            = "c"
  30. L_OP_DELETE            = "d"
  31.  
  32. L_DESC_PROGRAM      = "rserver - Manipulate NNTP virtual servers"
  33. L_DESC_GET            = "Get NNTP virtual server properties"
  34. L_DESC_SET            = "Set NNTP virtual server properties"
  35. L_DESC_CREATE        = "Create a new NNTP virtual server"
  36. L_DESC_DELETE       = "Delete an NNTP virtual server"
  37.  
  38. L_DESC_OPERATIONS    = "<operations>"
  39. L_DESC_SERVER       = "<server> Specify computer to configure"
  40. L_DESC_INSTANCE_ID  = "<virtual server id> Specify virtual server id"
  41. L_DESC_CL_POSTS        = "<true/false> Allow client posts"
  42. L_DESC_CL_SOFT        = "soft client post limit in bytes"
  43. L_DESC_CL_HARD        = "hard client post limit in bytes"
  44. L_DESC_FD_POSTS        = "<true/false> Allow feed posts"
  45. L_DESC_FD_SOFT        = "soft feed post limit in bytes"
  46. L_DESC_FD_HARD        = "hard feed post limit in bytes"
  47. L_DESC_SMTP_SERVER    = "SMTP address for moderated posts"
  48. L_DESC_MODERATOR_DOMAIN    = "Domain name for default moderators"
  49. L_DESC_UUCP_NAME        = "UUCP name of this server"
  50. L_DESC_CTRL_MSGS        = "<true/false> Process control messages"
  51. L_DESC_NNTPFILE            = "Internal Nntp server files location"
  52. L_DESC_ADMIN_EMAIL        = "Administrator email account"
  53. L_DESC_PICKUP            = "Pickup directory"
  54. L_DESC_FAILED_PICKUP    = "Failed pickup directory"
  55. L_DESC_DROP                = "Drop directory"
  56. L_DESC_NEWNEWS            = "<true/false> Disable NEWNEWS command"
  57. L_DESC_HONOR_MSGIDS        = "<true/false> Honor client msg IDs"
  58. L_DESC_PORT             = "TCP Port for client connections"
  59.  
  60. L_DESC_EXAMPLES         = "Examples:"
  61. L_DESC_EXAMPLE1         = "rserver.vbs -t g -v 1"
  62. L_DESC_EXAMPLE2         = "rserver.vbs -t s -v 1 -c true -l 1000000 -a administrator@mydomain.com"
  63. L_DESC_EXAMPLE3         = "rserver.vbs -t d -v 3"
  64. L_DESC_EXAMPLE4         = "rserver.vbs -t c -v 2 -n c:\news_content"
  65.  
  66. L_STR_SERVER_ID            = "Virtual server ID:"
  67. L_STR_CL_POSTS            = "Allow client posting?"
  68. L_STR_CL_SOFT            = "Client soft posting limit:"
  69. L_STR_CL_HARD            = "Client hard posting limit:"
  70. L_STR_FD_POSTS            = "Allow feed posting?"
  71. L_STR_FD_SOFT            = "Feed soft posting limit:"
  72. L_STR_FD_HARD            = "Feed hard posting limit:"
  73. L_STR_SMTP_SERVER        = "Smtp server:"
  74. L_STR_MODERATOR_DOMAIN    = "Default moderator domain:"
  75. L_STR_UUCP_NAME            = "UUCP name:"
  76. L_STR_CTRL_MSGS            = "Process control messages?"
  77. L_STR_NNTPFILE            = "Nntp file location:"
  78. L_STR_ADMIN_EMAIL        = "Administrator email address:"
  79. L_STR_PICKUP            = "Pickup directory:"
  80. L_STR_FAILED_PICKUP        = "Failed pickup directory:"
  81. L_STR_DROP                = "Drop directory:"
  82. L_STR_NEWNEWS            = "Disable NEWNEWS command?"
  83. L_STR_HONOR_MSGIDS        = "Honor client message IDs?"
  84. L_STR_PORT              = "TCP Port:"
  85.  
  86. L_STR_CREATED_VS        = "Successfully created the following virtual server:"
  87.  
  88. L_ERR_INVALID_INSTANCE_ID        = "Invalid instance identifier."
  89. L_ERR_CANT_DELETE_INSTANCE_ONE    = "You can't delete the default virtual server."
  90. L_ERR_MUST_SPECIFY_NNTPFILE        = "You must specify the nntp file location."
  91. L_ERR_INVALID_PORT              = "Invalid port number."
  92.  
  93. REM
  94. REM END LOCALIZATION
  95. REM
  96.  
  97. REM
  98. REM --- Globals ---
  99. REM
  100.  
  101. dim g_dictParms
  102. dim    g_admin
  103.  
  104. set g_dictParms = CreateObject ( "Scripting.Dictionary" )
  105.  
  106. REM
  107. REM --- Set argument defaults ---
  108. REM
  109.  
  110. g_dictParms(L_SWITCH_OPERATION)        = ""
  111. g_dictParms(L_SWITCH_SERVER)        = "localhost"
  112. g_dictParms(L_SWITCH_INSTANCE_ID)    = "1"
  113. g_dictParms(L_SWITCH_CL_POSTS)        = ""
  114. g_dictParms(L_SWITCH_CL_SOFT)        = ""
  115. g_dictParms(L_SWITCH_CL_HARD)        = ""
  116. g_dictParms(L_SWITCH_FD_POSTS)        = ""
  117. g_dictParms(L_SWITCH_FD_SOFT)        = ""
  118. g_dictParms(L_SWITCH_FD_HARD)        = ""
  119. g_dictParms(L_SWITCH_SMTP_SERVER)    = ""
  120. g_dictParms(L_SWITCH_MODERATOR_DOMAIN)    = ""
  121. g_dictParms(L_SWITCH_UUCP_NAME)        = ""
  122. g_dictParms(L_SWITCH_CTRL_MSGS)        = ""
  123. g_dictParms(L_SWITCH_NNTPFILE)        = ""
  124. g_dictParms(L_SWITCH_ADMIN_EMAIL)    = ""
  125. g_dictParms(L_SWITCH_PICKUP)        = ""
  126. g_dictParms(L_SWITCH_FAILED_PICKUP)    = ""
  127. g_dictParms(L_SWITCH_DROP)            = ""
  128. g_dictParms(L_SWITCH_NEWNEWS)        = ""
  129. g_dictParms(L_SWITCH_HONOR_MSGIDS)    = ""
  130. g_dictParms(L_SWITCH_PORT)            = ""
  131.  
  132. REM
  133. REM --- Begin Main Program ---
  134. REM
  135.  
  136. if NOT ParseCommandLine ( g_dictParms, WScript.Arguments ) then
  137.     usage
  138.     WScript.Quit ( 0 )
  139. end if
  140.  
  141. REM
  142. REM    Debug: print out command line arguments:
  143. REM
  144. REM switches = g_dictParms.keys
  145. REM args = g_dictParms.items
  146. REM
  147. REM
  148. REM for i = 0 to g_dictParms.Count - 1
  149. REM     WScript.echo switches(i) & " = " & args(i)
  150. REM next
  151. REM
  152.  
  153. server = g_dictParms(L_SWITCH_SERVER)
  154. instance = g_dictParms(L_SWITCH_INSTANCE_ID)
  155.  
  156. if NOT IsNumeric (instance) then
  157.     WScript.Echo L_ERR_INVALID_INSTANCE_ID
  158.     WScript.Quit 0
  159. end if
  160.  
  161. select case g_dictParms(L_SWITCH_OPERATION)
  162. case L_OP_GET
  163.     set g_admin = CreateAdsiObject ( server, instance )
  164.  
  165.     PrintVirtualServer g_admin
  166.  
  167. case L_OP_SET
  168.     set g_admin = CreateAdsiObject ( server, instance )
  169.  
  170.     SetProperties ( g_admin )
  171.  
  172.     g_admin.SetInfo
  173.     g_admin.GetInfo
  174.  
  175.     PrintVirtualServer g_admin
  176.  
  177. case L_OP_CREATE
  178.  
  179.     dim strNntpFile
  180.  
  181.     strNntpFile = g_dictParms ( L_SWITCH_NNTPFILE )
  182.  
  183.     if strNntpFile = "" then
  184.         WScript.Echo L_ERR_MUST_SPECIFY_NNTPFILE
  185.         WScript.Quit 0
  186.     end if
  187.  
  188.     set g_admin = CreateAdsiObject ( server, "" )
  189.  
  190.     set newinst = g_admin.Create ( "IIsNntpServer", instance )
  191.  
  192.     newinst.Put "ServerBindings", ":119:"
  193.     newinst.Put "NewsPickupDirectory", strNntpFile & "\Pickup" 
  194.     newinst.Put "NewsFailedPickupDirectory", strNntpFile & "\FailedPickup"
  195.     newinst.Put "NewsDropDirectory", strNntpFile & "\Drop"
  196.     SetProperties ( newinst )
  197.  
  198.     newinst.SetInfo
  199.  
  200.     set newvroot = newinst.Create ( "IIsNntpVirtualDir", "Root" )
  201.  
  202.     newvroot.SetInfo
  203.     newvroot.Put "Path", strNntpFile & "\root"
  204.     newvroot.Put "AccessFlags", 3
  205.     newvroot.SetInfo
  206.  
  207.     WScript.Echo L_STR_CREATED_VS
  208.     PrintVirtualServer newinst
  209.  
  210. case L_OP_DELETE
  211.     set g_admin = CreateAdsiObject ( server, "" )
  212.  
  213.     if instance = 1 then
  214.         WScript.Echo L_ERR_CANT_DELETE_INSTANCE_ONE
  215.         WScript.Quit 0
  216.     end if
  217.  
  218.     g_admin.Delete "IIsNntpServer", instance
  219.  
  220. case else
  221.     usage
  222.  
  223. end select
  224.  
  225. WScript.Quit 0
  226.  
  227. REM
  228. REM --- End Main Program ---
  229. REM
  230.  
  231. REM
  232. REM ParseCommandLine ( dictParameters, cmdline )
  233. REM     Parses the command line parameters into the given dictionary
  234. REM
  235. REM Arguments:
  236. REM     dictParameters  - A dictionary containing the global parameters
  237. REM     cmdline - Collection of command line arguments
  238. REM
  239. REM Returns - Success code
  240. REM
  241.  
  242. Function ParseCommandLine ( dictParameters, cmdline )
  243.     dim     fRet
  244.     dim     cArgs
  245.     dim     i
  246.     dim     strSwitch
  247.     dim     strArgument
  248.  
  249.     fRet    = TRUE
  250.     cArgs   = cmdline.Count
  251.     i       = 0
  252.     
  253.     do while (i < cArgs)
  254.  
  255.         REM
  256.         REM Parse the switch and its argument
  257.         REM
  258.  
  259.         if i + 1 >= cArgs then
  260.             REM
  261.             REM Not enough command line arguments - Fail
  262.             REM
  263.  
  264.             fRet = FALSE
  265.             exit do
  266.         end if
  267.  
  268.         strSwitch = cmdline(i)
  269.         i = i + 1
  270.  
  271.         strArgument = cmdline(i)
  272.         i = i + 1
  273.  
  274.         REM
  275.         REM Add the switch,argument pair to the dictionary
  276.         REM
  277.  
  278.         if NOT dictParameters.Exists ( strSwitch ) then
  279.             REM
  280.             REM Bad switch - Fail
  281.             REM
  282.  
  283.             fRet = FALSE
  284.             exit do
  285.         end if 
  286.  
  287.         dictParameters(strSwitch) = strArgument
  288.  
  289.     loop
  290.  
  291.     ParseCommandLine = fRet
  292. end function
  293.  
  294. REM
  295. REM Usage ()
  296. REM     prints out the description of the command line arguments
  297. REM
  298.  
  299. Sub Usage
  300.  
  301.     WScript.Echo L_DESC_PROGRAM
  302.     WScript.Echo vbTab & L_SWITCH_OPERATION & " " & L_DESC_OPERATIONS
  303.     WScript.Echo vbTab & vbTab & L_OP_GET & vbTab & L_DESC_GET
  304.     WScript.Echo vbTab & vbTab & L_OP_SET & vbTab & L_DESC_SET
  305.     WScript.Echo vbTab & vbTab & L_OP_CREATE & vbTab & L_DESC_CREATE
  306.     WScript.Echo vbTab & vbTab & L_OP_DELETE & vbTab & L_DESC_DELETE
  307.     WScript.Echo vbTab & L_SWITCH_SERVER & " " & L_DESC_SERVER
  308.     WScript.Echo vbTab & L_SWITCH_INSTANCE_ID & " " & L_DESC_INSTANCE_ID
  309.     WScript.Echo vbTab & L_SWITCH_CL_POSTS & " " & L_DESC_CL_POSTS
  310.     WScript.Echo vbTab & L_SWITCH_CL_SOFT & " " & L_DESC_CL_SOFT
  311.     WScript.Echo vbTab & L_SWITCH_CL_HARD & " " & L_DESC_CL_HARD
  312.     WScript.Echo vbTab & L_SWITCH_FD_POSTS & " " & L_DESC_FD_POSTS
  313.     WScript.Echo vbTab & L_SWITCH_FD_SOFT & " " & L_DESC_FD_SOFT
  314.     WScript.Echo vbTab & L_SWITCH_FD_HARD & " " & L_DESC_FD_HARD
  315.     WScript.Echo vbTab & L_SWITCH_SMTP_SERVER & " " & L_DESC_SMTP_SERVER
  316.     WScript.Echo vbTab & L_SWITCH_MODERATOR_DOMAIN & " " & L_DESC_MODERATOR_DOMAIN
  317.     WScript.Echo vbTab & L_SWITCH_UUCP_NAME & " " & L_DESC_UUCP_NAME
  318.     WScript.Echo vbTab & L_SWITCH_CTRL_MSGS & " " & L_DESC_CTRL_MSGS
  319.     WScript.Echo vbTab & L_SWITCH_NNTPFILE & " " & L_DESC_NNTPFILE
  320.     WScript.Echo vbTab & L_SWITCH_ADMIN_EMAIL & " " & L_DESC_ADMIN_EMAIL
  321.     WScript.Echo vbTab & L_SWITCH_PICKUP & " " & L_DESC_PICKUP
  322.     WScript.Echo vbTab & L_SWITCH_FAILED_PICKUP & " " & L_DESC_FAILED_PICKUP
  323.     WScript.Echo vbTab & L_SWITCH_DROP & " " & L_DESC_DROP
  324.     WScript.Echo vbTab & L_SWITCH_NEWNEWS & " " & L_DESC_NEWNEWS
  325.     WScript.Echo vbTab & L_SWITCH_HONOR_MSGIDS & " " & L_DESC_HONOR_MSGIDS
  326.     WScript.Echo vbTab & L_SWITCH_PORT & " " & L_DESC_PORT
  327.  
  328.     WScript.Echo
  329.     WScript.Echo L_DESC_EXAMPLES
  330.     WScript.Echo L_DESC_EXAMPLE1
  331.     WScript.Echo L_DESC_EXAMPLE2
  332.     WScript.Echo L_DESC_EXAMPLE3
  333.  
  334. end sub
  335.  
  336. Sub PrintVirtualServer ( admobj )
  337.  
  338.     dim strNntpFile
  339.  
  340.     strNntpFile = admobj.Get ( "ArticleTableFile" )
  341.  
  342.     WScript.Echo L_STR_SERVER_ID & " " & admobj.Name
  343.     WScript.Echo L_STR_CL_POSTS & " " & admobj.Get ( "AllowClientPosts" )
  344.     WScript.Echo L_STR_CL_SOFT & " " & admobj.Get ( "ClientPostSoftLimit" )
  345.     WScript.Echo L_STR_CL_HARD & " " & admobj.Get ( "ClientPostHardLimit" )
  346.     WScript.Echo L_STR_FD_POSTS & " " & admobj.Get ( "AllowFeedPosts" )
  347.     WScript.Echo L_STR_FD_SOFT & " " & admobj.Get ( "FeedPostSoftLimit" )
  348.     WScript.Echo L_STR_FD_HARD & " " & admobj.Get ( "FeedPostHardLimit" )
  349.     WScript.Echo L_STR_SMTP_SERVER & " " & admobj.Get ( "SmtpServer" )
  350.     WScript.Echo L_STR_MODERATOR_DOMAIN & " " & admobj.Get ( "DefaultModeratorDomain" )
  351.     WScript.Echo L_STR_UUCP_NAME & " " & admobj.Get ( "NntpUucpName" )
  352.     WScript.Echo L_STR_CTRL_MSGS & " " & admobj.Get ( "AllowControlMsgs" )
  353.     WScript.Echo L_STR_ADMIN_EMAIL & " " & admobj.Get ( "AdminEmail" )
  354.     WScript.Echo L_STR_PICKUP & " " & admobj.Get ( "NewsPickupDirectory" )
  355.     WScript.Echo L_STR_FAILED_PICKUP & " " & admobj.Get ( "NewsFailedPickupDirectory" )
  356.     WScript.Echo L_STR_DROP & " " & admobj.Get ( "NewsDropDirectory" )
  357.     WScript.Echo L_STR_NEWNEWS & " " & admobj.Get ( "DisableNewnews" )
  358.     WScript.Echo L_STR_HONOR_MSGIDS & " " & admobj.Get ( "HonorClientMsgIds" )
  359.     WScript.Echo L_STR_NNTPFILE & " " & StripFileNameFromPath ( strNntpFile )
  360.     WScript.Echo L_STR_PORT & " " & ExtractPortFromBinding ( admobj.Get ( "ServerBindings") ( 0 ) )
  361.  
  362. end sub
  363.  
  364. Sub SetProperties ( admobj )
  365.  
  366.     SetProp admobj, g_dictParms(L_SWITCH_CL_POSTS), "AllowClientPosts", TRUE
  367.     SetProp admobj, g_dictParms(L_SWITCH_CL_SOFT), "ClientPostSoftLimit", FALSE
  368.     SetProp admobj, g_dictParms(L_SWITCH_CL_HARD), "ClientPostHardLimit", FALSE
  369.     SetProp admobj, g_dictParms(L_SWITCH_FD_POSTS), "AllowFeedPosts", TRUE
  370.     SetProp admobj, g_dictParms(L_SWITCH_FD_SOFT), "FeedPostSoftLimit", FALSE
  371.     SetProp admobj, g_dictParms(L_SWITCH_FD_HARD), "FeedPostHardLimit", FALSE
  372.     SetProp admobj, g_dictParms(L_SWITCH_SMTP_SERVER), "SmtpServer", FALSE
  373.     SetProp admobj, g_dictParms(L_SWITCH_MODERATOR_DOMAIN), "DefaultModeratorDomain", FALSE
  374.     SetProp admobj, g_dictParms(L_SWITCH_UUCP_NAME), "NntpUucpName", FALSE
  375.     SetProp admobj, g_dictParms(L_SWITCH_CTRL_MSGS), "AllowControlMsgs", TRUE
  376.     SetProp admobj, g_dictParms(L_SWITCH_ADMIN_EMAIL), "AdminEmail", FALSE
  377.     SetProp admobj, g_dictParms(L_SWITCH_PICKUP), "NewsPickupDirectory", FALSE
  378.     SetProp admobj, g_dictParms(L_SWITCH_FAILED_PICKUP), "NewsFailedPickupDirectory", FALSE
  379.     SetProp admobj, g_dictParms(L_SWITCH_DROP), "NewsDropDirectory", FALSE
  380.     SetProp admobj, g_dictParms(L_SWITCH_NEWNEWS), "DisableNewnews", TRUE
  381.     SetProp admobj, g_dictParms(L_SWITCH_HONOR_MSGIDS), "HonorClientMsgIds", TRUE
  382.     dim port
  383.     dim bindings
  384.     dim IpAddress
  385.  
  386.     port = g_dictParms(L_SWITCH_PORT)
  387.     if port <> "" then
  388.         if NOT IsNumeric ( port ) then
  389.             WScript.Echo L_ERR_INVALID_PORT
  390.             WScript.Quit 0
  391.         end if
  392.  
  393.         bindings    = admobj.GetEx ( "ServerBindings" )
  394.         if IsEmpty ( bindings ) then
  395.             bindings = Array ( "" )
  396.             IpAddress = ""
  397.         else
  398.             IpAddress = ExtractIpAddressFromBinding ( bindings(0) )
  399.         end if 
  400.         bindings(0) = IpAddress & ":" & port & ":"
  401.         admobj.PutEx 2, "ServerBindings", bindings
  402.     end if
  403.  
  404.     dim strNntpFile
  405.  
  406.     strNntpFile = g_dictParms ( L_SWITCH_NNTPFILE )
  407.  
  408.     if strNntpFile <> "" then
  409.         SetProp admobj, strNntpFile & "\descrip.txt", "GroupHelpFile", FALSE
  410.         SetProp admobj, strNntpFile & "\group.lst", "GroupListFile", FALSE
  411.         SetProp admobj, strNntpFile & "\article.hsh", "ArticleTableFile", FALSE
  412.         SetProp admobj, strNntpFile & "\history.hsh", "HistoryTableFile", FALSE
  413.         SetProp admobj, strNntpFile & "\moderatr.txt", "ModeratorFile", FALSE
  414.         SetProp admobj, strNntpFile & "\xover.hsh", "XoverTableFile", FALSE
  415.         SetProp admobj, strNntpFile & "\prettynm.txt", "PrettyNamesFile", FALSE
  416.     end if
  417.  
  418. end sub
  419.  
  420. sub SetProp ( admobj, value, prop, isbool )
  421.  
  422.     REM
  423.     REM Debug code: print out the setting of values:
  424.     REM
  425.     REM WScript.Echo "Setting " & prop & " = " & value
  426.     REM
  427.  
  428.     if value <> "" then
  429.  
  430.         if isbool then
  431.             admobj.Put prop, CBool (value)
  432.         else
  433.             admobj.Put prop, (value)
  434.         end if
  435.  
  436.     end if
  437.  
  438. end sub
  439.  
  440. Function CreateAdsiObject ( strMachine, dwInstance )
  441.  
  442.     dim strObject
  443.     dim obj
  444.  
  445.     if dwInstance <> "" then
  446.         strObject = "IIS://" & strMachine & "/NntpSvc/" & dwInstance
  447.     else
  448.         strObject = "IIS://" & strMachine & "/NntpSvc"
  449.     end if
  450.  
  451.     set CreateAdsiObject = GetObject ( strObject )
  452.  
  453. end function
  454.  
  455. Function StripFileNameFromPath ( strPath )
  456.  
  457.     dim    i
  458.  
  459.     i = InStrRev ( strPath, "\" )
  460.     StripFileNameFromPath = Left ( strPath, i - 1 )
  461.  
  462. end function
  463.  
  464. Function ExtractPortFromBinding ( strBinding )
  465.     dim SplitArray
  466.  
  467.     SplitArray = split ( strBinding, ":" )
  468.     ExtractPortFromBinding = SplitArray ( 1 )
  469. end function
  470.  
  471. Function ExtractIpAddressFromBinding ( strBinding )
  472.     dim SplitArray
  473.  
  474.     SplitArray = split ( strBinding, ":" )
  475.     ExtractIpAddressFromBinding = SplitArray ( 0 )
  476. end function
  477.  
  478.  
  479.