home *** CD-ROM | disk | FTP | other *** search
/ Media Share 9 / MEDIASHARE_09.ISO / network / src_1218.zip / BOOTPCMD.C < prev    next >
C/C++ Source or Header  |  1991-05-31  |  17KB  |  707 lines

  1. /*************************************************/
  2. /* Center for Information Technology Integration */
  3. /*           The University of Michigan          */
  4. /*                    Ann Arbor                  */
  5. /*                                               */
  6. /* Dedicated to the public domain.               */
  7. /* Send questions to info@citi.umich.edu         */
  8. /*                                               */
  9. /* BOOTP is documented in RFC 951 and RFC 1048   */
  10. /*************************************************/
  11.  
  12.  
  13.  
  14. #include <stdio.h>
  15. #include <sys\types.h>
  16. #include <sys\stat.h>
  17. #include <time.h>
  18. #include <ctype.h>
  19. #ifdef    ANSIPROTO
  20. #include <stdarg.h>
  21. #endif
  22. #include "global.h"
  23. #include "config.h"
  24. #include "cmdparse.h"
  25. #include "bootpd.h"
  26. #include "netuser.h"
  27. #include "iface.h"
  28. #include "udp.h"
  29. #include "arp.h"
  30.  
  31. #define BP_DEFAULT_TAB "bootptab"
  32. #define BP_DEFAULT_LOG "bootplog"
  33. #define BP_DEFAULT_DIR "bpfiles"
  34. #define BP_DEFAULT_FILE "boot"
  35.  
  36. static char    *bootptab = BP_DEFAULT_TAB;
  37. static FILE    *bootfp;                 /* bootptab fp */
  38. static long     modtime;              /* last modification time of bootptab */
  39.  
  40. static char    bootplog[64] = BP_DEFAULT_LOG;
  41. static int    LogInFile = 0;         /* Should bp_log log in a file? */
  42. static int    LogOnScreen = 0;    /* Should bp_log log on screen? */
  43.  
  44. static char    *line;            /* line buffer for reading bootptab */
  45. static int     linenum;          /* current ilne number in bootptab */
  46.  
  47. extern int    Nhosts;          /* number of hosts in host structure */
  48. extern struct host hosts[MHOSTS];
  49.  
  50. extern char    homedir[64];      /* bootfile homedirectory */
  51. extern char    defaultboot[64];  /* default file to boot */
  52. extern int32   bp_DefaultDomainNS[BP_MAXDNS]; /* default domain name server */
  53. extern int    Nhosts;
  54. extern struct udp_cb *Bootpd_cb;
  55.  
  56.  
  57.  
  58.  
  59.  
  60. static int bp_Homedir            __ARGS((int argc,char *argv[],void *p));
  61. static int bp_DefaultFile        __ARGS((int argc,char *argv[],void *p));
  62. static int bp_DynamicRange       __ARGS((int argc,char *argv[],void *p));
  63. static int bp_donothing          __ARGS((int argc,char *argv[],void *p));
  64. static int bp_Host          __ARGS((int argc,char *argv[],void *p));
  65. static int bp_rmHost          __ARGS((int argc,char *argv[],void *p));
  66. static int bp_DomainNS          __ARGS((int argc,char *argv[],void *p));
  67. static int bp_Start                __ARGS((int argc,char *argv[],void *p));
  68. static int bp_Stop                 __ARGS((int argc,char *argv[],void *p));
  69. static int bp_logFile          __ARGS((int argc,char *argv[],void *p));
  70. static int bp_logScreen              __ARGS((int argc,char *argv[],void *p));
  71.  
  72.  
  73. void bootpd __ARGS((struct iface *iface, struct udp_cb *sock, int cnt));
  74.  
  75. static struct cmds BootpdCmds[] = {
  76.         "",             bp_donothing,           0, 0, NULLCHAR,
  77.     "start",     bp_Start,        0, 0, NULLCHAR,
  78.     "stop",        bp_Stop,        0, 0, NULLCHAR,
  79.     "dns",        bp_DomainNS,        0, 0, NULLCHAR,
  80.         "dynip",        bp_DynamicRange,         0, 0, NULLCHAR,
  81.     "host",        bp_Host,        0, 0, NULLCHAR,
  82.     "rmhost",    bp_rmHost,        0, 0, NULLCHAR,
  83.         "homedir",      bp_Homedir,              0, 0, NULLCHAR,
  84.         "defaultfile",  bp_DefaultFile,          0, 0, NULLCHAR,
  85.     "logfile",    bp_logFile,        0, 0, NULLCHAR,
  86.     "logscreen",    bp_logScreen,        0, 0, NULLCHAR,
  87.         NULLCHAR,       NULLFP,                 0, 0, NULLCHAR
  88. };
  89.  
  90.  
  91.  
  92. int 
  93. bootpdcmd (argc, argv, p)
  94. int argc;
  95. char *argv[];
  96. void *p;
  97. {
  98.     return subcmd (BootpdCmds, argc, argv, p);
  99. }
  100.  
  101.  
  102. /* Start up bootp service */
  103. static int
  104. bp_Start (argc,argv,p)
  105. int argc;
  106. char *argv[];
  107. void *p;
  108. {
  109.  
  110.         struct socket lsock;
  111.         time_t tloc;
  112.     char *usage = "bootpd start\n";
  113.  
  114.     if (argc != 1) {
  115.         tprintf (usage);
  116.         return (-1);
  117.     }
  118.  
  119.     time(&tloc);
  120.         bp_log ("\n\n####BOOTP server starting at %s\n", ctime(&tloc));
  121.  
  122.         lsock.address = INADDR_ANY;
  123.         lsock.port = IPPORT_BOOTPS;
  124.  
  125.         /* This way is better than recvfrom because it passes the iface in bootpd call */
  126.        /* Listen doesn't work for datagrams. */
  127.  
  128.         if (Bootpd_cb == NULLUDP) {
  129.                 if ((Bootpd_cb = open_udp(&lsock, bootpd)) == NULLUDP) {
  130.             tprintf ("bootpd: can't open_udp\n");    
  131.             return (-1);
  132.         }
  133.         }
  134.  
  135.         /*
  136.          * Read the bootptab file once immediately upon startup.
  137.          */
  138.  
  139.         da_init();
  140.  
  141.         readtab();
  142.  
  143.         return (0);
  144. }
  145.  
  146.  
  147.  
  148. /* Stop bootp service */
  149. static int
  150. bp_Stop (argc, argv, p)
  151. int argc;
  152. char *argv[];
  153. void *p;
  154. {
  155.     time_t now;
  156.     char *usage = "bootpd stop\n";
  157.  
  158.     if (argc != 1) {
  159.         tprintf (usage);
  160.         return -1;
  161.     }
  162.  
  163.     time (&now);
  164.  
  165.         Nhosts = 0;
  166.         da_shut();
  167.         readtab_shut();
  168.         del_udp (Bootpd_cb);
  169.         Bootpd_cb = NULLUDP;
  170.  
  171.     bp_log ("Bootpd shutdown %s", ctime (&now));
  172.         return (0);
  173. };
  174.  
  175.  
  176.  
  177. static int
  178. bp_logFile (argc, argv, p)
  179. int argc;
  180. char *argv[];
  181. void *p;
  182. {
  183.     int i;
  184.     time_t now;
  185.     char *usage = "bootpd logfile [<file_name> | default] [on | off] \n"; 
  186.  
  187.     time (&now);
  188.  
  189.     if (argc == 1) {
  190.         if (LogInFile)
  191.                     tprintf ("Bootpd logging to file '%s' turned on.\n", bootplog);
  192.         else 
  193.                     tprintf ("Bootpd logging to file '%s' turned off.\n", bootplog);
  194.     }
  195.     else {
  196.         for (i = 1; i < argc; i++) {
  197.  
  198.             if (strcmp ("?", argv[i]) == 0) 
  199.                 tprintf (usage);
  200.  
  201.             else if (strcmp ("off", argv[i]) == 0) {
  202.                 bp_log ("Stopping file logging at %s", ctime(&now));
  203.                 LogInFile = 0;
  204.             }
  205.             else if (strcmp ("on", argv[i]) == 0) {
  206.                 LogInFile = 1;
  207.                 bp_log ("Starting file logging at %s", ctime(&now));
  208.             }
  209.             else if (strcmp ("default", argv[i]) == 0) {
  210.                 strcpy (bootplog, BP_DEFAULT_LOG);
  211.                 bp_log ("File for logging set to %s\n", bootplog);
  212.             }
  213.             else {
  214.                 strcpy (bootplog, argv[1]);
  215.                 bp_log ("File for logging set to %s\n", bootplog);
  216.             }    
  217.         }
  218.     }
  219.     return 0;
  220. }
  221.  
  222.  
  223. static int
  224. bp_logScreen (argc, argv, p)
  225. int argc;
  226. char *argv[];
  227. void *p;
  228. {
  229.     char *usage = "bootpd logscreen [on | off]\n";    
  230.  
  231.         if (argc == 1)
  232.         if (LogOnScreen)    
  233.                     tprintf ("Bootpd logging on screen turned on.\n");
  234.         else 
  235.                     tprintf ("Bootpd logging on screen turned off.\n");
  236.  
  237.         else if (argc == 2)  {
  238.                 if  (strcmp ("on", argv[1]) == 0)
  239.                         LogOnScreen = 1;
  240.                 else if  (strcmp ("off", argv[1]) == 0)
  241.                         LogOnScreen = 0;
  242.         else tprintf (usage);
  243.     }
  244.     else tprintf (usage);
  245.     return 0;
  246. }
  247.  
  248.  
  249.  
  250.  
  251. static int
  252. bp_DomainNS (argc, argv, p)
  253. int argc;
  254. char *argv[];
  255. void *p;
  256. {
  257.     int a0, a1, a2, a3;
  258.     int i;
  259.     char *usage = "bootpd dns [<IP addr of domain name server>...]\n";
  260.  
  261.     if (argc == 1) {
  262.         tprintf ("Bootp domain name servers: ");
  263.         for (i=0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++) 
  264.             tprintf (" %s", inet_ntoa (bp_DefaultDomainNS[i]));
  265.         tprintf ("\n");
  266.         return (0);
  267.     }
  268.  
  269.     if (argc > 1) {
  270.         if ((argc == 2) && (strcmp ("?", argv[1]) == 0)) {
  271.             tprintf (usage);
  272.             return 0;
  273.         }
  274.             
  275.         /* A list of name servers has been given */
  276.         /* reset the domain name server list */
  277.         for (i= 0; i < BP_MAXDNS; i++) 
  278.             bp_DefaultDomainNS[i] = 0;
  279.  
  280.         /* get ip address */
  281.         for (i = 1; (i < argc) && (i < BP_MAXDNS); i++) {
  282.                     if (4 != sscanf (argv[i], "%d.%d.%d.%d", &a0, &a1, &a2, &a3)) {
  283.                                 tprintf("bad internet address: %s\n", argv[1], linenum);
  284.                 return  -1;
  285.                     }
  286.                 bp_DefaultDomainNS[i-1] = aton(argv[i]);
  287.         }
  288.     }
  289.     /* record for the loggers sake */
  290.     bp_log ("Bootp domain name servers: ");
  291.     for (i=0; (i < BP_MAXDNS) && (bp_DefaultDomainNS[i] != 0); i++) 
  292.         bp_log (" %s", inet_ntoa (bp_DefaultDomainNS[i]));
  293.     bp_log ("\n");
  294.     return 0;
  295. }
  296.  
  297.  
  298.  
  299. static int
  300. bp_rmHost (argc, argv, p)
  301. int argc;
  302. char *argv[];
  303. void *p;
  304. {
  305.     int i;
  306.     struct host *hp = NULLHOST;
  307.     struct host *cp = NULLHOST;
  308.     char *usage = "bootpd rmhost <host name>\n";
  309.  
  310.  
  311.     if (argc == 2) {
  312.         
  313.         /* Find the host record */
  314.         for (i=0; i < Nhosts; i++) {
  315.             if (strcmp (hosts[i].name, argv[1]) == 0) {
  316.                 hp = &(hosts[i]);
  317.                 break;
  318.             }
  319.         }    
  320.         /* Return if not found */
  321.         if (hp == NULLHOST) {
  322.             tprintf ("Host %s not in host tables.\n", argv[1]);
  323.             return -1;
  324.         }
  325.         bp_log ("Host %s removed from host table\n", hp->name);
  326.         cp = &(hosts [Nhosts - 1]);
  327.         if (hp < cp) 
  328.             memcpy ((char *) hp, (char *) cp, sizeof (struct host));
  329.         Nhosts--;
  330.         return 0;
  331.     }
  332.     else tprintf (usage);
  333.     return 0;
  334. }
  335.  
  336.  
  337. /*
  338.  * Printout the hosts table.
  339. */
  340. static void
  341. dumphosts()
  342. {
  343.         int i;
  344.         struct host *hp;
  345.     struct arp_type *at;
  346.  
  347.         tprintf ("\n\nStatus of host table\n");
  348.  
  349.         if (Nhosts == 0) {
  350.                 tprintf ("     No hosts in host table\n");
  351.                 return;
  352.         }
  353.         for (i = 0; i <= Nhosts-1; i++) {
  354.                 hp = &hosts[i];
  355.         at = &Arp_type[hp->htype];
  356.         
  357.                 tprintf ("%s  %s  %s  %s  '%s'\n",
  358.                         hp->name, ArpNames[hp->htype], (*at->format)(bp_ascii, hp->haddr),
  359.                         inet_ntoa ((int32)hp->iaddr.s_addr),
  360.                         hp->bootfile);
  361.  
  362.         }
  363. }
  364.  
  365.  
  366. static int
  367. bp_Host (argc, argv, p)
  368. int argc;
  369. char *argv[];
  370. void *p;
  371. {
  372.     struct host *hp;
  373.     int a0, a1, a2, a3;
  374.     struct arp_type *at;
  375.     char *usage = "bootpd host [<hostname> <hardware type> <hardware addr> <ip addr> [boot file]]\n";
  376.  
  377.     switch (argc) {
  378.     case 1:    
  379.         dumphosts();
  380.         break;
  381.     case 5:
  382.     case 6:
  383.     
  384.         hp = &hosts[Nhosts];
  385.         
  386.         /* get host name */
  387.         strncpy (hp->name, argv[1], sizeof (hp->name));
  388.  
  389.         /* get hardware type */
  390.         /* This code borrowed from Phil Karn's arpcmd.c */
  391.         /* This is a kludge. It really ought to be table driven */
  392.         switch(tolower(argv[2][0])){
  393.             case 'n':       /* Net/Rom pseudo-type */
  394.                 hp->htype = ARP_NETROM;
  395.                 break;
  396.             case 'e': /* "ether" */
  397.                 hp->htype = ARP_ETHER;
  398.                 break;
  399.             case 'a': /* "ax25" */
  400.                 hp->htype = ARP_AX25;
  401.                 break;
  402.             case 'm': /* "mac appletalk" */
  403.                 hp->htype = ARP_APPLETALK;
  404.                 break;
  405.             default:
  406.                 tprintf("unknown hardware type \"%s\"\n",argv[2]);
  407.                 return -1;
  408.         }
  409.  
  410.         at = &Arp_type[hp->htype];
  411.         if(at->scan == NULLFP){
  412.             return 1;
  413.         }
  414.         /* Destination address */
  415.         (*at->scan)(hp->haddr,argv[3]);
  416.  
  417.  
  418.         /* get ip address */
  419.                 if (4 != sscanf (argv[4], "%d.%d.%d.%d", &a0, &a1, &a2, &a3))
  420.                 {
  421.                         tprintf("bad internet address: %s\n", argv[1], linenum);
  422.                         return (0);
  423.                 }
  424.             hp->iaddr.s_addr = aton(argv[4]);
  425.  
  426.         /* get the bootpfile */
  427.         if (argc == 6) strncpy (hp->bootfile, argv[5], sizeof (hp->bootfile));
  428.         else hp->bootfile[0] = 0;
  429.  
  430.                 bp_log ("Host added: %s  %s  %s  %s  '%s'\n",
  431.                         hp->name, ArpNames[hp->htype], (*at->format)(bp_ascii, hp->haddr),
  432.                         inet_ntoa ((int32)hp->iaddr.s_addr),
  433.                         hp->bootfile);
  434.  
  435.  
  436.         Nhosts++;
  437.         break;
  438.     
  439.     default:
  440.         tprintf (usage);
  441.         break;
  442.     }
  443.     return 0;
  444. }
  445.  
  446.  
  447.  
  448. static int    
  449. bp_Homedir (argc, argv, p)
  450. int argc;
  451. char *argv[];
  452. void *p;
  453. {
  454.     char *usage = "bootpd homedir [<name of home directory> | default]\n";
  455.  
  456.     if (argc == 1)     
  457.         tprintf ("Bootp home directory: '%s'\n", homedir);
  458.     else if (argc == 2) {
  459.         if (strcmp (argv[1], "?") == 0)
  460.             tprintf (usage);
  461.         else if (strcmp (argv[1], "default") == 0) {
  462.             strcpy (homedir, BP_DEFAULT_DIR);
  463.             bp_log ("Bootp home directory set to: '%s'\n", homedir);
  464.         }
  465.         else {
  466.             strcpy (homedir, argv[1]);
  467.             bp_log ("Bootp home directory set to: '%s'\n", homedir);
  468.         }
  469.     }
  470.     else tprintf (usage);
  471.     return (0);
  472. };
  473.  
  474.  
  475.  
  476. static int
  477. bp_DefaultFile (argc, argv, p)
  478. int argc;
  479. char *argv[];
  480. void *p;
  481. {
  482.     char *usage = "bootpd defaultfile [<name of default boot file> | default]\n";
  483.  
  484.         if (argc == 1)
  485.                 tprintf ("Bootp default boot file:  '%s'\n", defaultboot);
  486.         else if (argc == 2) {
  487.         if (strcmp (argv[1], "?") == 0)
  488.                         tprintf (usage);
  489.                 else if (strcmp (argv[1], "default") == 0)
  490.                         strcpy (defaultboot, BP_DEFAULT_FILE);
  491.                 else {
  492.                         strcpy (defaultboot, argv[1]);
  493.                     bp_log ("Bootp default boot file set to:  '%s'\n", defaultboot);
  494.         }
  495.         }
  496.     else
  497.                 tprintf (usage);
  498.  
  499.     return  (0);
  500. };
  501.  
  502.  
  503. static int
  504. bp_DynamicRange (argc, argv, p)
  505. int argc;
  506. char *argv[];
  507. void *p;
  508. {
  509.     int i0, i1, i2, i3;
  510.     int32 start, end;
  511.     struct iface *iface;
  512.     char *usage = "bootpd dynip [<net name> | <netname>  <IP address> <IP address> | <netname> off]\n";
  513.  
  514.     if (argc == 1) {
  515.         da_status (NULLIF);
  516.         return 0;
  517.     }
  518.     if ((argc == 2) && (strcmp ("?", argv[1]) == 0)) {
  519.             tprintf (usage);
  520.             return 0;
  521.     }
  522.  
  523.     /* get the interface */
  524.     iface = if_lookup (argv[1]);
  525.     if (iface == NULLIF) {
  526.         tprintf ("network '%s' not found\n", argv[1]);
  527.         return  (-1);
  528.     }
  529.     if (argc == 2) {
  530.         da_status (iface);
  531.         return 0;
  532.     }
  533.     if (argc == 3) {
  534.         if (strcmp ("off", argv[2]) == 0) 
  535.             da_done_net (iface);
  536.         else tprintf (usage);
  537.     }
  538.     else if (argc == 4) {
  539.         
  540.             /* get ip address */
  541.                 /* check the ip address - isaddr isn't a ka9q function */
  542.                 if ((4 != sscanf (argv[2], "%d.%d.%d.%d", &i0, &i1, &i2, &i3)) || 
  543.             (i0 > 255) || (i1 > 255) || (i2 > 255) || (i3 > 255)
  544.         )
  545.                 {
  546.                     tprintf("bad internet address: %s\n", argv[2], linenum);
  547.                         return (-1);
  548.                 }
  549.  
  550.                 if ((4 != sscanf (argv[3], "%d.%d.%d.%d", &i0, &i1, &i2, &i3)) || 
  551.             (i0 > 255) || (i1 > 255) || (i2 > 255) || (i3 > 255)
  552.         )
  553.                 {
  554.                     tprintf("bad internet address: %s\n", argv[3], linenum);
  555.                         return (-1);
  556.                 }
  557.  
  558.                    start = aton(argv[2]);
  559.                 end = aton(argv[3]);
  560.  
  561.         da_serve_net (iface, start, end);
  562.  
  563.     }
  564.     else {
  565.         tprintf (usage);
  566.         return (0);
  567.     }
  568.  
  569.     return (0);
  570. };
  571.  
  572.  
  573. static int
  574. bp_donothing (argc, argv, p)
  575. int argc;
  576. char *argv[];
  577. void *p;
  578. {
  579.         return (0);
  580. }
  581.  
  582.  
  583. /*
  584.  * Read bootptab database file.  Avoid rereading the file if the
  585.  * write date hasn't changed since the last time we read it.
  586.  */
  587. int
  588. readtab()
  589. {
  590.         struct stat st;
  591.  
  592.         /* If the file hasn't been opened, open it. */
  593.         if (bootfp == 0) {
  594.                 if ((bootfp = fopen(bootptab, "r")) == NULLFILE) {
  595.                         bp_log("Can't open bootptab file: %s\n", bootptab);
  596.                         return (-1);
  597.                 }
  598.         }
  599.  
  600.         /* Only reread if modified */
  601.         stat (bootptab, &st);
  602.         if (st.st_mtime == modtime && st.st_nlink) {
  603.                 return (0); /* hasnt been modified or deleted yet */
  604.         }
  605.         /* It's been changed, reread. */
  606.  
  607.         if ((bootfp = fopen(bootptab, "r")) == NULLFILE) {
  608.                 bp_log("Can't open %s\n", bootptab);
  609.                 return (-1);
  610.         }
  611.         fstat(fileno(bootfp), &st);
  612.         bp_log("(re)reading %s\n", bootptab);
  613.         modtime = st.st_mtime;
  614.  
  615.         /*
  616.          * read and parse each line in the file.
  617.          */
  618.  
  619.     line = mallocw(BUFSIZ);    
  620.     
  621.     while (fgets(line, BUFSIZ, bootfp) != NULLCHAR) {
  622.         linenum++;
  623.  
  624.  
  625.         if ((line[0] == 0) || (line[0] == '#') || (line[0] == ' '))
  626.             continue;
  627.  
  628.                 if (cmdparse (BootpdCmds, line, NULL) ==  -1)
  629.                          continue;
  630.  
  631.         }
  632.         fclose(bootfp);
  633.     free (line);
  634.     return (0);
  635. }
  636.  
  637. void
  638. readtab_shut()
  639. {
  640.     modtime = 0;
  641. }
  642.  
  643. /*
  644.  * log an error message
  645.  *
  646.  */
  647. #if    defined(ANSIPROTO)
  648. void
  649. bp_log(char *fmt,...)
  650. {
  651.         FILE *fp;
  652.     va_list ap;
  653.  
  654.         if (LogOnScreen) {
  655.         va_start(ap,fmt);
  656.         vprintf(fmt, ap);
  657.         va_end(ap);
  658.         fflush (stdout);
  659.     }
  660.         if (LogInFile) {
  661.                 if ((fp = fopen(bootplog, "a+")) == NULL) {
  662.                         tprintf ("Cannot open bootplog.\n");
  663.                         return;
  664.                 }
  665.         va_start(ap,fmt);
  666.         vfprintf(fp, fmt, ap);
  667.         va_end(ap);
  668.                 fflush(fp);
  669.                 fclose(fp);
  670.         }
  671. }
  672. #else
  673. void
  674. bp_log(fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7, argv8, argv9, argv10)
  675. char *fmt;
  676. char *argv1;
  677. char *argv2;
  678. char *argv3;
  679. char *argv4;
  680. char *argv5;
  681. char *argv6;
  682. char *argv7;
  683. char *argv8;
  684. char *argv9;
  685. char *argv10;
  686. {
  687.         FILE *fp;
  688.  
  689.         if (LogOnScreen) {
  690.                 printf(fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7,
  691.                         argv8, argv9, argv10);
  692.         fflush (stdout);
  693.     }
  694.  
  695.         if (LogInFile) {
  696.                 if ((fp = fopen(bootplog, "a+")) == NULL) {
  697.                         tprintf ("Cannot open bootplog.\n");
  698.                         return;
  699.                 }
  700.                 fprintf(fp, fmt, argv1, argv2, argv3, argv4, argv5, argv6, argv7,
  701.                         argv8, argv9, argv10);
  702.                 fflush (fp);
  703.                 fclose(fp);
  704.         }
  705. }
  706. #endif
  707.