Ejemplo n.º 1
0
int main (int argc, char *argv[]) {
/*************/
/* image I/O */
/*************/
	CONC_BLOCK	conc_block;			/* conc i/o control block */
	FILE            *imgfp, *ROIfp, *outfp, *keyfp;
	char            imgroot[MAXL], imgfile[MAXL], ROIroot[MAXL], ROIfile[MAXL], outfile[MAXL] = "", keyfile[MAXL], tmpfile[MAXL];

/********************/
/* iamge processing */
/********************/
	int             imgdim[4], ROIdim[4], diedim[3], vdim, imgori, ROIori, isbig, isbigm;
	int		lendie = LENDIE, ncrit = NCRIT, ndie, mdie, *mdielist;
	int		ix, iy, iz, jx, jy, jz;
	float           imgvox[3], ROIvox[3];
        float           *imgt, *imgr;
	DIEDAT		*diedat;

/*************************/
/* timeseries processing */
/*************************/
	char		*format;
	double		tol = TOL, **b, *lambda, **v;
	int		nnez, nred;

/***********/
/* utility */
/***********/
	char            *ptr, command[2*MAXL];
	double		q;
	int             c, i, j, k, l, m, n;

/*********/
/* flags */
/*********/
	int		outtype = 0;
	int		conc_flag = 0;
	int             status = 0;
	int		count_zero = 0;
	int		defined;			/* test on imgt and imgr voxels */
	int		test_svd = 1;
	int		H_flag = 0;
	int		K_flag = 0;
	int		V_flag = 0;

/**************/
/* debug code */
/**************/
	double		**bcopy, **e, errmax;

	setprog (program, argv);
	if (!(format = (char *) calloc (1024, sizeof (char)))) errm (program);
/************************/
/* process command line */
/************************/
	for (j = 0, i = 1; i < argc; i++) {
		if (*argv[i] == '-') {
			strcpy (command, argv[i]); ptr = command;
			while (c = *ptr++) switch (c) {
				case 'Z': count_zero++;			break; 
				case 'V': V_flag++;			break; 
				case 'H': H_flag++;			break; 
				case 'K': K_flag++;			break; 
				case 'O': outtype = atoi (ptr);		*ptr = '\0'; break; 
				case 'l': lendie = atoi (ptr);		*ptr = '\0'; break; 
				case 'f': strncpy (format, ptr, 1023);	*ptr = '\0'; break; 
				case 'n': ncrit = atoi (ptr);		*ptr = '\0'; break; 
				case 't': tol = atof (ptr);		*ptr = '\0'; break; 
				case 'o': strcpy (outfile, ptr);	*ptr = '\0'; break; 
				default:				break;
			}
		} else switch (j) {
			case 0:	getroot (argv[i], imgroot);
				conc_flag = (strstr (argv[i], ".conc") == argv[i] + strlen (imgroot));
                						j++; break;
                	case 1: getroot (argv[i], ROIroot);	j++; break;
			default:				break;
		}
	}
	if (j < 2) usage (program);
	if (lendie <= 0 || ncrit <= 0) usage (program);

	if (strlen (outfile)) {
		if (!(outfp = fopen (outfile, "w"))) errw (program, outfile);
		printf ("Writing: %s\n", outfile);
	} else {
		outfp = stdout;
	}
	if (H_flag) write_command_line (outfp, argc, argv);

/**************************************/
/* get input 4dfp dims and open files */
/**************************************/
	if (conc_flag) {
		conc_init_quiet (&conc_block, program);
		conc_open_quiet (&conc_block, imgroot);
		strcpy (imgfile, conc_block.lstfile);
		for (k = 0; k < 4; k++) imgdim[k] = conc_block.imgdim[k];
		for (k = 0; k < 3; k++) imgvox[k] = conc_block.voxdim[k];
		imgori = conc_block.orient;
		isbig  = conc_block.isbig;
	} else {
		sprintf (imgfile, "%s.4dfp.img", imgroot);
		if (get_4dfp_dimoe_quiet (imgfile, imgdim, imgvox, &imgori, &isbig) < 0) errr (program, imgfile);
		if (!(imgfp = fopen (imgfile, "rb"))) errr (program, imgfile);
	}

/*****************/
/* alloc buffers */
/*****************/
	vdim = imgdim[0]*imgdim[1]*imgdim[2];
	if (!(imgt = (float *) malloc (vdim*sizeof (float)))) errm (program);
	if (!(imgr = (float *) malloc (vdim*sizeof (float)))) errm (program);

/****************************/
/* prepare dice from ROIimg */
/****************************/
	sprintf (ROIfile, "%s.4dfp.img", ROIroot);
	if (get_4dfp_dimoe_quiet (ROIfile, ROIdim, ROIvox, &ROIori, &isbigm) < 0) errr (program, ROIfile);
	status = ROIori - imgori;
	for (k = 0; k < 3; k++) status |= (imgdim[k] != ROIdim[k]);
	for (k = 0; k < 3; k++) status |= (fabs (imgvox[k] - ROIvox[k]) > 0.0001);
	if (status) {
		fprintf (stderr, "%s: %s %s vdim mismatch\n", program, imgroot, ROIroot);
		exit (-1);
	}
	fprintf (stdout, "Reading: %s\n", ROIfile);
	if (!(ROIfp = fopen (ROIfile, "rb"))
	|| eread (imgr, vdim, isbigm, ROIfp)
	|| fclose (ROIfp)) errr (program, ROIfile);

	ndie = 1; for (k = 0; k < 3; k++) {
		diedim[k] = imgdim[k]/lendie;
		ndie *= diedim[k];
	}
	if (0) printf ("diedim %d %d %d ndie %d\n", diedim[0], diedim[1], diedim[2], ndie);
	if (!(diedat = (DIEDAT *) calloc (ndie, sizeof (DIEDAT)))) errm (program);
	j = 0;		/* index of die */
	for (iz = 0; iz < imgdim[2] - (imgdim[2] % lendie); iz += lendie) {
	for (iy = 0; iy < imgdim[1] - (imgdim[1] % lendie); iy += lendie) {
	for (ix = 0; ix < imgdim[0] - (imgdim[0] % lendie); ix += lendie) {
	if (0) printf ("ix, iy, iz, j %5d%5d%5d%10d\n", ix, iy, iz, j);
		diedat[j].fx = ix + lendie/2. - 0.5;
		diedat[j].fy = iy + lendie/2. - 0.5;
		diedat[j].fz = iz + lendie/2. - 0.5;
		j++;
	}}}
	assert (j == ndie);

	for (iz = 0; iz < imgdim[2] - (imgdim[2] % lendie); iz++) {jz = iz/lendie;
	for (iy = 0; iy < imgdim[1] - (imgdim[1] % lendie); iy++) {jy = iy/lendie;
	for (ix = 0; ix < imgdim[0] - (imgdim[0] % lendie); ix++) {jx = ix/lendie;
		j = jx + diedim[0]*(jy + diedim[1]*jz);		/* index of die */
		i = ix + imgdim[0]*(iy + imgdim[1]*iz);		/* index of voxel */
		defined = isnormal (imgr[i]) && imgr[i] != (float) 1.e-37;
		if (defined) {
			if (diedat[j].n == diedat[j].ma) {
				diedat[j].ma += INCR;
				if (!diedat[j].p) {
					diedat[j].p =  		    malloc (diedat[j].ma*sizeof (int));
				} else {
					diedat[j].p = realloc (diedat[j].p, diedat[j].ma*sizeof (int));
				}
				if (!diedat[j].p) errm (program);
			}
			diedat[j].p[diedat[j].n++] = i;
		}
		i++;
	}}}

/********************/
/* create mdie list */
/********************/
	for (mdie = j = 0; j < ndie; j++) {
		if (V_flag) printf ("j diedat[j].n %5d %5d\n", j, diedat[j].n);
		if (diedat[j].n >= ncrit) mdie++;
	}
	printf ("defined die count = %d\n", mdie);
	if (!(mdielist = (int *) calloc (mdie, sizeof (int)))) errm (program);
	for (mdie = j = 0; j < ndie; j++) if (diedat[j].n >= ncrit) mdielist[mdie++] = j;

	if (K_flag) {
/*********************************/
/* create voxel address key file */
/*********************************/
		sprintf (tmpfile, "temp%d", getpid ());
		if (!(keyfp = fopen (tmpfile, "w"))) errw (program, tmpfile);
		for (m = 0; m < mdie; m++) {
			j = mdielist[m];			/* index of die with at least ncrit voxels in ROI mask */
			fprintf (keyfp, "%10.2f%10.2f%10.2f\n", diedat[j].fx, diedat[j].fy, diedat[j].fz);
		}
		if (fclose (keyfp)) errw (program, tmpfile);
		strcpy (keyfile, imgroot);
		if (!(ptr = strrchr (keyfile, '/'))) ptr = keyfile; else ptr++;
		strcpy (keyfile, ptr);
		strcat (keyfile, "_");
		strcpy (command, ROIroot);
		if (!(ptr = strrchr (command, '/'))) ptr = command; else ptr++;
		strcat (keyfile, ptr);
		strcat (keyfile, "_indices.txt");
		if (!(keyfp = fopen (keyfile, "w"))) errw (program, keyfile);
		printf ("Writing: %s\n", keyfile);
		write_command_line (keyfp, argc, argv);
		if (fclose (keyfp)) errw (program, keyfile);
		sprintf (command, "index2atl %s %s >> %s",
			(conc_flag) ? conc_block.imgfile0[0] : imgroot, tmpfile, keyfile);
		printf ("%s\n", command);
		status |= system (command);
		remove (tmpfile);
		if (status) exit (-1);
	}

/****************/
/* parse format */
/****************/
	if (!(format = (char *) realloc (format, (imgdim[3] + 1)*sizeof (char)))) errm (program);
	if (strlen (format)) {
		if (k = expandf (format, imgdim[3] + 1)) exit (-1);
		if (strlen (format) != imgdim[3]) {
			fprintf (stderr, "%s: %s frame-to-count format length mismatch\n", program, imgroot);
			exit (-1); 
		}
		for (nnez = k = 0; k < imgdim[3]; k++) if (format[k] != 'x') nnez++;
	} else {
		nnez = imgdim[3];
		for (i = 0; i < imgdim[3]; i++) format[i] = '+';
	}
	printf ("%s\n", format);
	printf ("%s: time series defined for %d frames, %d exluded\n", program, imgdim[3], imgdim[3] - nnez);

/***************************/
/* allocate memory for svd */
/***************************/
	if (!(lambda = (double *) calloc (nnez, sizeof (double)))) errm (program);
	v = calloc_double2 (nnez, nnez);
	b = calloc_double2 (mdie, nnez);

/***********/
/* process */
/***********/
	printf ("Reading: %s\n", imgfile);
	for (n = k = 0; k < imgdim[3]; k++) {
		if (conc_flag) {
			conc_read_vol (&conc_block, imgt);
		} else {
			if (eread (imgt, vdim, isbig, imgfp)) errr (program, imgfile);
		}
		for (m = 0; m < mdie; m++) {
			j = mdielist[m];			/* index of die with at least ncrit voxels in ROI mask */
			diedat[j].v = diedat[j].m = 0;		/* initialize anew for each frame */
			for (l = 0; l < diedat[j].n; l++) {	/* loop on voxels */
				i = diedat[j].p[l];		/* index into imgt */
				defined = isnormal (imgt[i]) && imgt[i] != (float) 1.e-37;
				if (count_zero && imgt[i] == 0.) defined = 1;
				if (defined) {
					diedat[j].v += imgt[i];
					diedat[j].m++;
				}
			}
			if (diedat[j].m < ncrit) {
				fprintf (stderr, "%s: %s contains undefined voxels within %s mask\n",
								program, imgfile, ROIroot);
				exit (-1);
			}
			diedat[j].v /= diedat[j].m;
		}
		if (format[k] != 'x') {
			for (m = 0; m < mdie; m++) {
				j = mdielist[m];		/* index of die with at least ncrit voxels in ROI mask */
				b[m][n] = diedat[j].v;
			}
			n++;					/* increment non-skipped frames count */
		}
	}
	assert (n == nnez);
	free (imgt); free (imgr);
	if (conc_flag) {
		conc_free (&conc_block);
	} else {
		if (fclose (imgfp)) errr (program, imgfile);
	}
	if (!outtype) goto DONE;

/*****************************************************/
/* make timeseries zero mean (but not unit variance) */
/*****************************************************/
	if (outtype > 1) for (j = 0; j < mdie; j++) dzeromean (b[j], nnez);

/*******************************/
/* output extracted timeseries */
/*******************************/
	if (outtype == 1 || outtype == 2) {
		for (n = k = 0; k < imgdim[3]; k++) {
			for (m = 0; m < mdie; m++) fprintf (outfp, " %9.4f", (format[k] != 'x') ? b[m][n] : 0.0);
			if (format[k] != 'x') n++;
			if (mdie) fprintf (outfp, "\n");
		}
		assert (n == nnez);
		goto DONE;
	}

	if (test_svd) {
		bcopy = calloc_double2 (mdie, nnez);
   	   	    e = calloc_double2 (mdie, nnez);
		for (j = 0; j < mdie; j++) for (i = 0; i < nnez; i++) bcopy[j][i] = b[j][i];
	}
   	        dsvdcmp0 (b, mdie, nnez, lambda, v);
	nred = ndsvdcmp0 (b, mdie, nnez, lambda, v, tol);
	if (H_flag) {
		fprintf (outfp, "#reduced dimensionality = %d\n", nred);
		fprintf (outfp, "#lambda after ndsvdcmp0");
		for (i = 0; i < nred; i++) fprintf (outfp, " %.2f", lambda[i]); fprintf (outfp, "\n");
	}
	if (test_svd) {
		for (j = 0; j < mdie; j++) for (i = 0; i < nnez; i++) for (k = 0; k < nred; k++) {
			e[j][i] += v[i][k]*lambda[k]*b[j][k];
		}
		for (errmax = i = 0; i < nnez; i++) for (j = 0; j < mdie; j++) {
			q = fabs (e[j][i] - bcopy[j][i]);
			if (q > errmax) errmax = q;
		}
		if (H_flag) fprintf (outfp, "#maximum error = %.3g\n", errmax);
		free_double2 (bcopy); free_double2 (e);
	}
	for (k = 0; k < nnez; k++) for (j = 0; j < nred; j++) {
		v[k][j] *= (outtype == 3) ? lambda[j] : sqrt ((double) nnez);
	}
/******************************/
/* output computed timeseries */
/******************************/
	if (outtype == 3 || outtype == 4) {
		for (n = k = 0; k < imgdim[3]; k++) {
			for (m = 0; m < nred; m++) fprintf (outfp, " %9.4f", (format[k] != 'x') ? v[n][m] : 0.0);
			if (format[k] != 'x') n++;
			if (mdie) fprintf (outfp, "\n");
		}
		assert (n == nnez);
	}

/*********************/
/* clean up and exit */
/*********************/
DONE:	if (strlen (outfile)) if (fclose (outfp)) errw (program, outfile);
	free_double2 (v); free_double2 (b); free (lambda);
	free (mdielist);
	for (j = 0; j < ndie; j++) if (diedat[j].p) free (diedat[j].p);
	free (diedat);
	free (format);
	exit (status);
}
Ejemplo n.º 2
0
int main(int argc, char *argv[])
{
  int i;
  BOOL verbose = TRUE;
  BOOL very_verbose = TRUE;
  F64 start_time = 0.0;
  F64 full_start_time = 0.0;
  const CHAR* xml_output_file = 0;
  BOOL one_report_per_file = FALSE;
  U32 num_pass = 0;
  U32 num_fail = 0;
  U32 num_warning = 0;

  fprintf(stderr, "This is version '%s' of the LAS validator. Please contact\n", "GRiD-1");
  fprintf(stderr, "me at '*****@*****.**' if you disagree with\n");
  fprintf(stderr, "validation reports, want additional checks, or find bugs as\n");
  fprintf(stderr, "the software is still under development. Your feedback will\n");
  fprintf(stderr, "help to finish it sooner.\n");

  LASreadOpener lasreadopener;

  if (argc == 1)
  {
    fprintf(stderr,"lasvalidate.exe is best run with arguments in the command line\n");
    char file_name[256];
    fprintf(stderr,"enter input LAS file name: "); fgets(file_name, 256, stdin);
    file_name[strlen(file_name)-1] = '\0';
    lasreadopener.set_file_name(file_name);
    fprintf(stderr,"enter output XML file name: "); fgets(file_name, 256, stdin);
    file_name[strlen(file_name)-1] = '\0';
    xml_output_file = strdup(file_name);
  }

  for (i = 1; i < argc; i++)
  {
    if (strcmp(argv[i],"-version") == 0)
    {
      fprintf(stderr, "\nlasvalidate %d with LASread (v %d.%d %d) and LAScheck (v %d.%d %d) by rapidlasso GmbH\n", VALIDATE_VERSION, LASREAD_VERSION_MAJOR, LASREAD_VERSION_MINOR, LASREAD_BUILD_DATE, LASCHECK_VERSION_MAJOR, LASCHECK_VERSION_MINOR, LASCHECK_BUILD_DATE);
      byebye(LAS_VALIDATE_SUCCESS);
    }
    else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"-help") == 0)
    {
      lasreadopener.usage();
      usage(LAS_VALIDATE_SUCCESS);
    }
    else if (strcmp(argv[i],"-v") == 0 || strcmp(argv[i],"-verbose") == 0)
    {
      verbose = TRUE;
    }
    else if (strcmp(argv[i],"-vv") == 0 || strcmp(argv[i],"-very_verbose") == 0)
    {
      verbose = TRUE;
      very_verbose = TRUE;
    }
    else if (strcmp(argv[i],"-i") == 0)
    {
      if ((i+1) >= argc)
      {
        fprintf(stderr,"ERROR: '%s' needs at least 1 argument: file_name or wild_card\n", argv[i]);
        usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX);
      }
      i+=1;
      do
      {
        lasreadopener.add_file_name(argv[i]);
        i+=1;
      } while (i < argc && *argv[i] != '-');
      i-=1;
    }
    else if (strcmp(argv[i],"-irec") == 0)
    {
      if ((i+1) >= argc)
      {
        fprintf(stderr,"ERROR: '%s' needs at least 1 argument: directory_name\n", argv[i]);
        usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX);
      }
      i+=1;
      do
      {
        lasreadopener.add_directory(argv[i], TRUE);
        i+=1;
      } while (i < argc && *argv[i] != '-');
      i-=1;
    }
    else if (strcmp(argv[i],"-stdin") == 0)
    {
      lasreadopener.set_piped(TRUE);
    }
    else if (strcmp(argv[i],"-lof") == 0)
    {
      if ((i+1) >= argc)
      {
        fprintf(stderr,"ERROR: '%s' needs 1 argument: list_of_files\n", argv[i]);
        usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX);
      }
      FILE* file = fopen(argv[i+1], "r");
      if (file == 0)
      {
        fprintf(stderr, "ERROR: cannot open '%s'\n", argv[i+1]);
        return FALSE;
      }
      char line[1024];
      while (fgets(line, 1024, file))
      {
        // find end of line
        int len = strlen(line) - 1;
        // remove extra white spaces and line return at the end 
        while (len > 0 && ((line[len] == '\n') || (line[len] == ' ') || (line[len] == '\t') || (line[len] == '\012')))
        {
          line[len] = '\0';
          len--;
        }
        lasreadopener.add_file_name(line);
      }
      fclose(file);
      i+=1;
    }
    else if (strcmp(argv[i],"-o") == 0)
    {
      i++;
      xml_output_file = argv[i];
    }
    else if (strcmp(argv[i],"-oxml") == 0)
    {
      one_report_per_file = TRUE;
    }
    else
    {
      fprintf(stderr, "ERROR: cannot understand argument '%s'\n", argv[i]);
      usage(LAS_VALIDATE_WRONG_COMMAND_LINE_SYNTAX);
    }
  }

  // in verbose mode we measure the total time

  if (verbose) full_start_time = taketime();

  // check input

  if (!lasreadopener.is_active())
  {
    fprintf(stderr,"ERROR: no input specified\n");
    byebye(LAS_VALIDATE_NO_INPUT_SPECIFIED, argc == 1);
  }

  // output logging

  XMLwriter xmlwriter;

  if (lasreadopener.is_active())
  {
    if (xml_output_file)
    {
      one_report_per_file = FALSE;
    }
    else if (!one_report_per_file)
    {
      xml_output_file = "validate.xml";
    }
  }

  // maybe we are doing one summary report

  if (xml_output_file)
  {
    if (!xmlwriter.open(xml_output_file, "LASvalidator"))
    {
      byebye(LAS_VALIDATE_WRITE_PERMISSION_ERROR, argc == 1);
    }
  }

  // accumulated pass

  U32 total_pass = VALIDATE_PASS;

  // possibly loop over multiple input files

  while (lasreadopener.is_active())
  {
    // in very verbose mode we measure the time for each file

    if (very_verbose) start_time = taketime();

    // open lasreader

    LASreader* lasreader = lasreadopener.open();
    if (lasreader == 0)
    {
      fprintf(stderr, "ERROR: could not open lasreader\n");
      byebye(LAS_VALIDATE_INPUT_FILE_NOT_FOUND, argc == 1);
    }

    // get a pointer to the header
    LASheader* lasheader = &lasreader->header;

    // maybe we are doing one report per file

    if (one_report_per_file)
    {
      int len = strlen(lasreadopener.get_path());
      CHAR* current_xml_output_file = (CHAR*)malloc(len + 5);
      strcpy(current_xml_output_file, lasreadopener.get_path());
      current_xml_output_file[len-4] = '_';
      current_xml_output_file[len-3] = 'L';
      current_xml_output_file[len-2] = 'V';
      current_xml_output_file[len-1] = 'S';
      current_xml_output_file[len  ] = '.';
      current_xml_output_file[len+1] = 'x';
      current_xml_output_file[len+2] = 'm';
      current_xml_output_file[len+3] = 'l';
      current_xml_output_file[len+4] = '\0';
      if (!xmlwriter.open(current_xml_output_file, "LASvalidator"))
      {
        byebye(LAS_VALIDATE_WRITE_PERMISSION_ERROR, argc == 1);
      }
      free(current_xml_output_file);
    }

    // start a new report
    
    xmlwriter.begin("report");

    // report description of file

    xmlwriter.beginsub("file");
    xmlwriter.write("name", lasreadopener.get_file_name());
    xmlwriter.write("path", lasreadopener.get_path());
    CHAR temp[32];
    sprintf(temp, "%d.%d", lasheader->version_major, lasheader->version_minor);
    xmlwriter.write("version", temp);
    strncpy(temp, lasheader->system_identifier, 32);
    temp[31] = '\0';
    xmlwriter.write("system_identifier", temp);
    strncpy(temp, lasheader->generating_software, 32);
    temp[31] = '\0';
    xmlwriter.write("generating_software", temp);
    xmlwriter.write("point_data_format", lasheader->point_data_format);

    CHAR crsdescription[512];
    strcpy(crsdescription, "not valid or not specified");

    if (lasheader->fails == 0)
    {
      // header was loaded. now parse and check.

      LAScheck lascheck(lasheader);

      while (lasreader->read_point())
      {
        lascheck.parse(&lasreader->point);
      }

      // check header and points and get CRS description

      lascheck.check(lasheader, crsdescription);
    }

    xmlwriter.write("CRS", crsdescription);
    xmlwriter.endsub("file");    

    // report the verdict

    U32 pass = (lasheader->fails ? VALIDATE_FAIL : VALIDATE_PASS);
    if (lasheader->warnings) pass |= VALIDATE_WARNING;

    xmlwriter.beginsub("summary");
    xmlwriter.write((pass == VALIDATE_PASS ? "pass" : ((pass & VALIDATE_FAIL) ? "fail" : "warning")));
    xmlwriter.endsub("summary");

    // report details (if necessary)

    if (pass != VALIDATE_PASS)
    {
      xmlwriter.beginsub("details");
      for (i = 0; i < lasheader->fail_num; i+=2)
      {
        xmlwriter.write(lasheader->fails[i], "fail", lasheader->fails[i+1]);
      }
      for (i = 0; i < lasheader->warning_num; i+=2)
      {
        xmlwriter.write(lasheader->warnings[i], "warning", lasheader->warnings[i+1]);
      }
      xmlwriter.endsub("details");
      total_pass |= pass;
      if (pass & VALIDATE_FAIL)
      {
        num_fail++;
      }
      else
      {
        num_warning++;
      }
    }
    else
    {
      num_pass++;
    }

    // end the report

    xmlwriter.end("report");

    // maybe we are doing one report per file

    if (one_report_per_file)
    {
      // report the total verdict

      xmlwriter.begin("total");
      xmlwriter.write((total_pass == VALIDATE_PASS ? "pass" : ((total_pass & VALIDATE_FAIL) ? "fail" : "warning")));
      xmlwriter.beginsub("details");
      xmlwriter.write("pass", num_pass);
      xmlwriter.write("warning", num_warning);
      xmlwriter.write("fail", num_fail);
      xmlwriter.endsub("details");
      xmlwriter.end("total");

      num_pass = 0;
      num_warning = 0;
      num_fail = 0;

      // write which validator was used

      write_version(xmlwriter);

      // write which command line was used

      write_command_line(xmlwriter, argc, argv);

      // close the LASvalidator XML output file

      xmlwriter.close("LASvalidator");
    }

    lasreader->close();
    delete lasreader;

    // in very verbose mode we report the time for each file

    if (very_verbose)
    {
      fprintf(stderr,"needed %.2f sec for '%s'\n", taketime()-start_time, lasreadopener.get_file_name());
      start_time = taketime();
    }
  }

  // maybe we are doing one summary report

  if (!one_report_per_file)
  {
    // report the total verdict

    xmlwriter.begin("total");
    xmlwriter.write((total_pass == 0 ? "pass" : ((total_pass & 1) ? "fail" : "warning")));
    xmlwriter.beginsub("details");
    xmlwriter.write("pass", num_pass);
    xmlwriter.write("warning", num_warning);
    xmlwriter.write("fail", num_fail);
    xmlwriter.endsub("details");
    xmlwriter.end("total");

    // write which validator was used

    write_version(xmlwriter);

    // write which command line was used

    write_command_line(xmlwriter, argc, argv);

    // close the LASvalidator XML output file

    xmlwriter.close("LASvalidator");
  }

  // in verbose mode we report the total time

  if (verbose && (lasreadopener.get_file_name_number() > 1))
  {
    fprintf(stderr,"done. total time %.2f sec.\n", taketime()-full_start_time);
  }

  byebye(argc==1);

  return 0;
}
Ejemplo n.º 3
0
/* process HTTP or SSDP requests */
int
main(int argc, char * * argv)
{
	int i;
	int shttpl = -1;	/* socket for HTTP */
	int sudp = -1;		/* IP v4 socket for receiving SSDP */
#ifdef ENABLE_IPV6
	int sudpv6 = -1;	/* IP v6 socket for receiving SSDP */
#endif
#ifdef ENABLE_NATPMP
	int * snatpmp = NULL;
#endif
#ifdef ENABLE_NFQUEUE
	int nfqh = -1;
#endif
#ifdef USE_IFACEWATCHER
	int sifacewatcher = -1;
#endif

	int * snotify = NULL;
	int addr_count;
	LIST_HEAD(httplisthead, upnphttp) upnphttphead;
	struct upnphttp * e = 0;
	struct upnphttp * next;
	fd_set readset;	/* for select() */
	fd_set writeset;
	struct timeval timeout, timeofday, lasttimeofday = {0, 0};
	int max_fd = -1;
#ifdef USE_MINIUPNPDCTL
	int sctl = -1;
	LIST_HEAD(ctlstructhead, ctlelem) ctllisthead;
	struct ctlelem * ectl;
	struct ctlelem * ectlnext;
#endif
	struct runtime_vars v;
	/* variables used for the unused-rule cleanup process */
	struct rule_state * rule_list = 0;
	struct timeval checktime = {0, 0};
	struct lan_addr_s * lan_addr;
#ifdef ENABLE_6FC_SERVICE
	unsigned int next_pinhole_ts;
#endif

	if(init(argc, argv, &v) != 0)
		return 1;
	/* count lan addrs */
	addr_count = 0;
	for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
		addr_count++;
	if(addr_count > 0) {
#ifndef ENABLE_IPV6
		snotify = calloc(addr_count, sizeof(int));
#else
		/* one for IPv4, one for IPv6 */
		snotify = calloc(addr_count * 2, sizeof(int));
#endif
	}
#ifdef ENABLE_NATPMP
	if(addr_count > 0) {
		snatpmp = malloc(addr_count * sizeof(int));
		for(i = 0; i < addr_count; i++)
			snatpmp[i] = -1;
	}
#endif

	LIST_INIT(&upnphttphead);
#ifdef USE_MINIUPNPDCTL
	LIST_INIT(&ctllisthead);
#endif

	if(
#ifdef ENABLE_NATPMP
        !GETFLAG(ENABLENATPMPMASK) &&
#endif
        !GETFLAG(ENABLEUPNPMASK) ) {
		syslog(LOG_ERR, "Why did you run me anyway?");
		return 0;
	}

	syslog(LOG_INFO, "Starting%s%swith external interface %s",
#ifdef ENABLE_NATPMP
	       GETFLAG(ENABLENATPMPMASK) ? " NAT-PMP " : " ",
#else
	       " ",
#endif
	       GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "",
	       ext_if_name);

	if(GETFLAG(ENABLEUPNPMASK))
	{

		/* open socket for HTTP connections. Listen on the 1st LAN address */
		shttpl = OpenAndConfHTTPSocket((v.port > 0) ? v.port : 0);
		if(shttpl < 0)
		{
			syslog(LOG_ERR, "Failed to open socket for HTTP. EXITING");
			return 1;
		}
		if(v.port <= 0) {
			struct sockaddr_in sockinfo;
			socklen_t len = sizeof(struct sockaddr_in);
			if (getsockname(shttpl, (struct sockaddr *)&sockinfo, &len) < 0) {
				syslog(LOG_ERR, "getsockname(): %m");
				return 1;
			}
			v.port = ntohs(sockinfo.sin_port);
		}
		syslog(LOG_NOTICE, "HTTP listening on port %d", v.port);
#ifdef ENABLE_IPV6
		if(find_ipv6_addr(NULL, ipv6_addr_for_http_with_brackets, sizeof(ipv6_addr_for_http_with_brackets)) > 0) {
			syslog(LOG_NOTICE, "HTTP IPv6 address given to control points : %s",
			       ipv6_addr_for_http_with_brackets);
		} else {
			memcpy(ipv6_addr_for_http_with_brackets, "[::1]", 6);
			syslog(LOG_WARNING, "no HTTP IPv6 address");
		}
#endif

		/* open socket for SSDP connections */
		sudp = OpenAndConfSSDPReceiveSocket(0);
		if(sudp < 0)
		{
			syslog(LOG_NOTICE, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd");
			if(SubmitServicesToMiniSSDPD(lan_addrs.lh_first->str, v.port) < 0) {
				syslog(LOG_ERR, "Failed to connect to MiniSSDPd. EXITING");
				return 1;
			}
		}
#ifdef ENABLE_IPV6
		sudpv6 = OpenAndConfSSDPReceiveSocket(1);
		if(sudpv6 < 0)
		{
			syslog(LOG_WARNING, "Failed to open socket for receiving SSDP (IP v6).");
		}
#endif

		/* open socket for sending notifications */
		if(OpenAndConfSSDPNotifySockets(snotify) < 0)
		{
			syslog(LOG_ERR, "Failed to open sockets for sending SSDP notify "
		                "messages. EXITING");
			return 1;
		}

#ifdef USE_IFACEWATCHER
		/* open socket for kernel notifications about new network interfaces */
		if (sudp >= 0)
		{
			sifacewatcher = OpenAndConfInterfaceWatchSocket();
			if (sifacewatcher < 0)
			{
				syslog(LOG_ERR, "Failed to open socket for receiving network interface notifications");
			}
		}
#endif
	}

#ifdef ENABLE_NATPMP
	/* open socket for NAT PMP traffic */
	if(GETFLAG(ENABLENATPMPMASK))
	{
		if(OpenAndConfNATPMPSockets(snatpmp) < 0)
		{
			syslog(LOG_ERR, "Failed to open sockets for NAT PMP.");
		} else {
			syslog(LOG_NOTICE, "Listening for NAT-PMP traffic on port %u",
			       NATPMP_PORT);
		}
#if 0
		ScanNATPMPforExpiration();
#endif
	}
#endif

	/* for miniupnpdctl */
#ifdef USE_MINIUPNPDCTL
	sctl = OpenAndConfCtlUnixSocket("/var/run/miniupnpd.ctl");
#endif

#ifdef ENABLE_NFQUEUE
	if ( nfqueue != -1 && n_nfqix > 0) {
		nfqh = OpenAndConfNFqueue();
		if(nfqh < 0) {
			syslog(LOG_ERR, "Failed to open fd for NFQUEUE.");
			return 1;
		} else {
			syslog(LOG_NOTICE, "Opened NFQUEUE %d",nfqueue);
		}
	}
#endif

	/* main loop */
	while(!quitting)
	{
		/* Correct startup_time if it was set with a RTC close to 0 */
		if((startup_time<60*60*24) && (time(NULL)>60*60*24))
		{
			set_startup_time(GETFLAG(SYSUPTIMEMASK));
		}
		/* send public address change notifications if needed */
		if(should_send_public_address_change_notif)
		{
			syslog(LOG_DEBUG, "should send external iface address change notification(s)");
#ifdef ENABLE_NATPMP
			if(GETFLAG(ENABLENATPMPMASK))
				SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count);
#endif
#ifdef ENABLE_EVENTS
			if(GETFLAG(ENABLEUPNPMASK))
			{
				upnp_event_var_change_notify(EWanIPC);
			}
#endif
			should_send_public_address_change_notif = 0;
		}
		/* Check if we need to send SSDP NOTIFY messages and do it if
		 * needed */
		if(gettimeofday(&timeofday, 0) < 0)
		{
			syslog(LOG_ERR, "gettimeofday(): %m");
			timeout.tv_sec = v.notify_interval;
			timeout.tv_usec = 0;
		}
		else
		{
			/* the comparaison is not very precise but who cares ? */
			if(timeofday.tv_sec >= (lasttimeofday.tv_sec + v.notify_interval))
			{
				if (GETFLAG(ENABLEUPNPMASK))
					SendSSDPNotifies2(snotify,
				                  (unsigned short)v.port,
				                  v.notify_interval << 1);
				memcpy(&lasttimeofday, &timeofday, sizeof(struct timeval));
				timeout.tv_sec = v.notify_interval;
				timeout.tv_usec = 0;
			}
			else
			{
				timeout.tv_sec = lasttimeofday.tv_sec + v.notify_interval
				                 - timeofday.tv_sec;
				if(timeofday.tv_usec > lasttimeofday.tv_usec)
				{
					timeout.tv_usec = 1000000 + lasttimeofday.tv_usec
					                  - timeofday.tv_usec;
					timeout.tv_sec--;
				}
				else
				{
					timeout.tv_usec = lasttimeofday.tv_usec - timeofday.tv_usec;
				}
			}
		}
		/* remove unused rules */
		if( v.clean_ruleset_interval
		  && (timeofday.tv_sec >= checktime.tv_sec + v.clean_ruleset_interval))
		{
			if(rule_list)
			{
				remove_unused_rules(rule_list);
				rule_list = NULL;
			}
			else
			{
				rule_list = get_upnp_rules_state_list(v.clean_ruleset_threshold);
			}
			memcpy(&checktime, &timeofday, sizeof(struct timeval));
		}
		/* Remove expired port mappings, based on UPnP IGD LeaseDuration
		 * or NAT-PMP lifetime) */
		if(nextruletoclean_timestamp
		  && ((unsigned int)timeofday.tv_sec >= nextruletoclean_timestamp))
		{
			syslog(LOG_DEBUG, "cleaning expired Port Mappings");
			get_upnp_rules_state_list(0);
		}
		if(nextruletoclean_timestamp
		  && ((unsigned int)timeout.tv_sec >= (nextruletoclean_timestamp - timeofday.tv_sec)))
		{
			timeout.tv_sec = nextruletoclean_timestamp - timeofday.tv_sec;
			timeout.tv_usec = 0;
			syslog(LOG_DEBUG, "setting timeout to %u sec",
			       (unsigned)timeout.tv_sec);
		}
#ifdef ENABLE_NATPMP
#if 0
		/* Remove expired NAT-PMP mappings */
		while(nextnatpmptoclean_timestamp
		     && (timeofday.tv_sec >= nextnatpmptoclean_timestamp + startup_time))
		{
			/*syslog(LOG_DEBUG, "cleaning expired NAT-PMP mappings");*/
			if(CleanExpiredNATPMP() < 0) {
				syslog(LOG_ERR, "CleanExpiredNATPMP() failed");
				break;
			}
		}
		if(nextnatpmptoclean_timestamp
		  && timeout.tv_sec >= (nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec))
		{
			/*syslog(LOG_DEBUG, "setting timeout to %d sec",
			       nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec);*/
			timeout.tv_sec = nextnatpmptoclean_timestamp + startup_time - timeofday.tv_sec;
			timeout.tv_usec = 0;
		}
#endif
#endif
#ifdef ENABLE_6FC_SERVICE
		/* Clean up expired IPv6 PinHoles */
		next_pinhole_ts = 0;
		upnp_clean_expired_pinholes(&next_pinhole_ts);
		if(next_pinhole_ts &&
		   timeout.tv_sec >= (int)(next_pinhole_ts - timeofday.tv_sec)) {
			timeout.tv_sec = next_pinhole_ts - timeofday.tv_sec;
			timeout.tv_usec = 0;
		}
#endif

		/* select open sockets (SSDP, HTTP listen, and all HTTP soap sockets) */
		FD_ZERO(&readset);
		FD_ZERO(&writeset);

		if (sudp >= 0)
		{
			FD_SET(sudp, &readset);
			max_fd = MAX( max_fd, sudp);
#ifdef USE_IFACEWATCHER
			if (sifacewatcher >= 0)
			{
				FD_SET(sifacewatcher, &readset);
				max_fd = MAX(max_fd, sifacewatcher);
			}
#endif
		}
		if (shttpl >= 0)
		{
			FD_SET(shttpl, &readset);
			max_fd = MAX( max_fd, shttpl);
		}
#ifdef ENABLE_IPV6
		if (sudpv6 >= 0)
		{
			FD_SET(sudpv6, &readset);
			max_fd = MAX( max_fd, sudpv6);
		}
#endif

#ifdef ENABLE_NFQUEUE
		if (nfqh >= 0)
		{
			FD_SET(nfqh, &readset);
			max_fd = MAX( max_fd, nfqh);
		}
#endif

		i = 0;	/* active HTTP connections count */
		for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
		{
			if(e->socket >= 0)
			{
				if(e->state <= EWaitingForHttpContent)
					FD_SET(e->socket, &readset);
				else if(e->state == ESendingAndClosing)
					FD_SET(e->socket, &writeset);
				else
					continue;
				max_fd = MAX(max_fd, e->socket);
				i++;
			}
		}
		/* for debug */
#ifdef DEBUG
		if(i > 1)
		{
			syslog(LOG_DEBUG, "%d active incoming HTTP connections", i);
		}
#endif
#ifdef ENABLE_NATPMP
		for(i=0; i<addr_count; i++) {
			if(snatpmp[i] >= 0) {
				FD_SET(snatpmp[i], &readset);
				max_fd = MAX( max_fd, snatpmp[i]);
			}
		}
#endif
#ifdef USE_MINIUPNPDCTL
		if(sctl >= 0) {
			FD_SET(sctl, &readset);
			max_fd = MAX( max_fd, sctl);
		}

		for(ectl = ctllisthead.lh_first; ectl; ectl = ectl->entries.le_next)
		{
			if(ectl->socket >= 0) {
				FD_SET(ectl->socket, &readset);
				max_fd = MAX( max_fd, ectl->socket);
			}
		}
#endif

#ifdef ENABLE_EVENTS
		upnpevents_selectfds(&readset, &writeset, &max_fd);
#endif

		if(select(max_fd+1, &readset, &writeset, 0, &timeout) < 0)
		{
			if(quitting) goto shutdown;
			if(errno == EINTR) continue; /* interrupted by a signal, start again */
			syslog(LOG_ERR, "select(all): %m");
			syslog(LOG_ERR, "Failed to select open sockets. EXITING");
			return 1;	/* very serious cause of error */
		}
#ifdef USE_MINIUPNPDCTL
		for(ectl = ctllisthead.lh_first; ectl;)
		{
			ectlnext =  ectl->entries.le_next;
			if((ectl->socket >= 0) && FD_ISSET(ectl->socket, &readset))
			{
				char buf[256];
				int l;
				l = read(ectl->socket, buf, sizeof(buf));
				if(l > 0)
				{
					/*write(ectl->socket, buf, l);*/
					write_command_line(ectl->socket, argc, argv);
					write_option_list(ectl->socket);
					write_permlist(ectl->socket, upnppermlist, num_upnpperm);
					write_upnphttp_details(ectl->socket, upnphttphead.lh_first);
					write_ctlsockets_list(ectl->socket, ctllisthead.lh_first);
					write_ruleset_details(ectl->socket);
#ifdef ENABLE_EVENTS
					write_events_details(ectl->socket);
#endif
					/* close the socket */
					close(ectl->socket);
					ectl->socket = -1;
				}
				else
				{
					close(ectl->socket);
					ectl->socket = -1;
				}
			}
			if(ectl->socket < 0)
			{
				LIST_REMOVE(ectl, entries);
				free(ectl);
			}
			ectl = ectlnext;
		}
		if((sctl >= 0) && FD_ISSET(sctl, &readset))
		{
			int s;
			struct sockaddr_un clientname;
			struct ctlelem * tmp;
			socklen_t clientnamelen = sizeof(struct sockaddr_un);
			//syslog(LOG_DEBUG, "sctl!");
			s = accept(sctl, (struct sockaddr *)&clientname,
			           &clientnamelen);
			syslog(LOG_DEBUG, "sctl! : '%s'", clientname.sun_path);
			tmp = malloc(sizeof(struct ctlelem));
			tmp->socket = s;
			LIST_INSERT_HEAD(&ctllisthead, tmp, entries);
		}
#endif
#ifdef ENABLE_EVENTS
		upnpevents_processfds(&readset, &writeset);
#endif
#ifdef ENABLE_NATPMP
		/* process NAT-PMP packets */
		for(i=0; i<addr_count; i++)
		{
			if((snatpmp[i] >= 0) && FD_ISSET(snatpmp[i], &readset))
			{
				ProcessIncomingNATPMPPacket(snatpmp[i]);
			}
		}
#endif
		/* process SSDP packets */
		if(sudp >= 0 && FD_ISSET(sudp, &readset))
		{
			/*syslog(LOG_INFO, "Received UDP Packet");*/
			ProcessSSDPRequest(sudp, (unsigned short)v.port);
		}
#ifdef ENABLE_IPV6
		if(sudpv6 >= 0 && FD_ISSET(sudpv6, &readset))
		{
			syslog(LOG_INFO, "Received UDP Packet (IPv6)");
			ProcessSSDPRequest(sudpv6, (unsigned short)v.port);
		}
#endif
#ifdef USE_IFACEWATCHER
		/* process kernel notifications */
		if (sifacewatcher >= 0 && FD_ISSET(sifacewatcher, &readset))
			ProcessInterfaceWatchNotify(sifacewatcher);
#endif

		/* process active HTTP connections */
		/* LIST_FOREACH macro is not available under linux */
		for(e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)
		{
			if(e->socket >= 0)
			{
				if(FD_ISSET(e->socket, &readset) ||
				   FD_ISSET(e->socket, &writeset))
				{
					Process_upnphttp(e);
				}
			}
		}
		/* process incoming HTTP connections */
		if(shttpl >= 0 && FD_ISSET(shttpl, &readset))
		{
			int shttp;
			socklen_t clientnamelen;
#ifdef ENABLE_IPV6
			struct sockaddr_storage clientname;
			clientnamelen = sizeof(struct sockaddr_storage);
#else
			struct sockaddr_in clientname;
			clientnamelen = sizeof(struct sockaddr_in);
#endif
			shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen);
			if(shttp<0)
			{
				/* ignore EAGAIN, EWOULDBLOCK, EINTR, we just try again later */
				if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
					syslog(LOG_ERR, "accept(http): %m");
			}
			else
			{
				struct upnphttp * tmp = 0;
				char addr_str[64];

				sockaddr_to_string((struct sockaddr *)&clientname, addr_str, sizeof(addr_str));
				syslog(LOG_INFO, "HTTP connection from %s", addr_str);
				/* Create a new upnphttp object and add it to
				 * the active upnphttp object list */
				tmp = New_upnphttp(shttp);
				if(tmp)
				{
#ifdef ENABLE_IPV6
					if(clientname.ss_family == AF_INET)
					{
						tmp->clientaddr = ((struct sockaddr_in *)&clientname)->sin_addr;
					}
					else if(clientname.ss_family == AF_INET6)
					{
						struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&clientname;
						if(IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr))
						{
							memcpy(&tmp->clientaddr,
							       &addr->sin6_addr.s6_addr[12],
							       4);
						}
						else
						{
							tmp->ipv6 = 1;
							memcpy(&tmp->clientaddr_v6,
							       &addr->sin6_addr,
							       sizeof(struct in6_addr));
						}
					}
#else
					tmp->clientaddr = clientname.sin_addr;
#endif
					LIST_INSERT_HEAD(&upnphttphead, tmp, entries);
				}
				else
				{
					syslog(LOG_ERR, "New_upnphttp() failed");
					close(shttp);
				}
			}
		}
#ifdef ENABLE_NFQUEUE
		/* process NFQ packets */
		if(nfqh >= 0 && FD_ISSET(nfqh, &readset))
		{
			/* syslog(LOG_INFO, "Received NFQUEUE Packet");*/
			ProcessNFQUEUE(nfqh);
		}
#endif
		/* delete finished HTTP connections */
		for(e = upnphttphead.lh_first; e != NULL; )
		{
			next = e->entries.le_next;
			if(e->state >= EToDelete)
			{
				LIST_REMOVE(e, entries);
				Delete_upnphttp(e);
			}
			e = next;
		}

	}	/* end of main loop */

shutdown:
	/* close out open sockets */
	while(upnphttphead.lh_first != NULL)
	{
		e = upnphttphead.lh_first;
		LIST_REMOVE(e, entries);
		Delete_upnphttp(e);
	}

	if (sudp >= 0) close(sudp);
	if (shttpl >= 0) close(shttpl);
#ifdef ENABLE_IPV6
	if (sudpv6 >= 0) close(sudpv6);
#endif
#ifdef USE_IFACEWATCHER
	if(sifacewatcher >= 0) close(sifacewatcher);
#endif
#ifdef ENABLE_NATPMP
	for(i=0; i<addr_count; i++) {
		if(snatpmp[i]>=0)
		{
			close(snatpmp[i]);
			snatpmp[i] = -1;
		}
	}
#endif
#ifdef USE_MINIUPNPDCTL
	if(sctl>=0)
	{
		close(sctl);
		sctl = -1;
		if(unlink("/var/run/miniupnpd.ctl") < 0)
		{
			syslog(LOG_ERR, "unlink() %m");
		}
	}
#endif

	if (GETFLAG(ENABLEUPNPMASK))
	{
#ifndef ENABLE_IPV6
		if(SendSSDPGoodbye(snotify, addr_count) < 0)
#else
		if(SendSSDPGoodbye(snotify, addr_count * 2) < 0)
#endif
		{
			syslog(LOG_ERR, "Failed to broadcast good-bye notifications");
		}
#ifndef ENABLE_IPV6
		for(i = 0; i < addr_count; i++)
#else
		for(i = 0; i < addr_count * 2; i++)
#endif
			close(snotify[i]);
	}

	if(pidfilename && (unlink(pidfilename) < 0))
	{
		syslog(LOG_ERR, "Failed to remove pidfile %s: %m", pidfilename);
	}

	/* delete lists */
	while(lan_addrs.lh_first != NULL)
	{
		lan_addr = lan_addrs.lh_first;
		LIST_REMOVE(lan_addrs.lh_first, list);
		free(lan_addr);
	}

#ifdef ENABLE_NATPMP
	free(snatpmp);
#endif
	free(snotify);
	closelog();
#ifndef DISABLE_CONFIG_FILE
	freeoptions();
#endif

	return 0;
}