示例#1
0
int get_category(FILE * fd, char *type, struct Categories *labels)
{
    long cat;
    char *lbl;

    R_stabilize();		/* force out all graphics */
    do {
	fprintf(stdout, "\n");
	cat = get_cat(type);
	lbl = get_label(cat, labels);
	fprintf(stdout, "%ld [%s]\n", cat,
		*lbl ? lbl : G_get_cat((CELL) cat, labels));
    } while (!G_yes(_("Look ok? "), 1));
    if (*lbl)
	G_set_cat((CELL) cat, lbl, labels);

    fprintf(fd, "= %ld %s\n", cat, lbl);
    return (0);
}
示例#2
0
/*!
  \brief Write data to GRASS ASCII vector format

  Prints message if some features without category are skipped.

  \param[out] ascii  pointer to the output ASCII file
  \param[out] att    att file (< version 5 only)
  \param Map    pointer to Map_info structure
  \param ver    version number 4 or 5
  \param format format GV_ASCII_FORMAT_POINT or GV_ASCII_FORMAT_STD
  \param dp     number of significant digits
  \param fs     field separator
  \param region_flag check region
  \param type   feature type filter
  \param field  field number
  \param Clist  list of categories to filter features or NULL
  \param where  SQL select where statement to filter features or NULL
  \param column_names array of columns to be included to the output or NULL
                 "*" as the first item in the array indicates all columns
  \param header TRUE to print also header

  \return number of written features
  \return -1 on error
*/
int Vect_write_ascii(FILE *ascii,
		     FILE *att, struct Map_info *Map, int ver,
		     int format, int dp, char *fs, int region_flag, int type,
		     int field, const struct cat_list *Clist, const char* where,
		     const char **column_names, int header)
{
    int ltype, ctype, i, cat, line, left, right, found;
    double *xptr, *yptr, *zptr, x, y;
    static struct line_pnts *Points;
    struct line_cats *Cats, *ACats;
    char *xstring, *ystring, *zstring;
    size_t xsize, ysize, zsize;
    struct Cell_head window;
    struct ilist *fcats;
    int count, n_skipped;

    /* where || columns */
    struct field_info *Fi;
    dbDriver *driver;
    dbValue value;
    dbHandle handle;
    int *cats, ncats, more;
    dbTable *Table;
    dbString dbstring;
    dbColumn *Column;
    dbValue *Value;
    char *buf;
    size_t bufsize;
    dbCursor cursor;
    /* columns */
    char **columns;
    int *coltypes;
    char *all_columns;
    
    Fi = NULL;
    driver = NULL;
    columns = NULL;
    coltypes = NULL;
    all_columns = NULL;
    
    G_zero(&value, sizeof(dbValue));
    db_init_string(&dbstring);

    xstring = NULL;
    ystring = NULL;
    zstring = NULL;
    xsize = 0;
    ysize = 0;
    zsize = 0;
    buf = NULL;
    bufsize = 0;

    /* get the region */
    G_get_window(&window);

    count = ncats = 0;
    xstring = ystring = zstring = NULL;
    cats = NULL;
    
    if (field > 0 && (where || column_names)) {
	Fi = Vect_get_field(Map, field);
	if (!Fi) {
	    G_fatal_error(_("Database connection not defined for layer %d"),
			  field);
	}

	driver = db_start_driver(Fi->driver);
	if (!driver)
	    G_fatal_error(_("Unable to start driver <%s>"), Fi->driver);
	
	db_init_handle(&handle);
	db_set_handle(&handle, Fi->database, NULL);
	
	if (db_open_database(driver, &handle) != DB_OK)
	    G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
			  Fi->database, Fi->driver);
	
	/* select cats (sorted array) */
	ncats = db_select_int(driver, Fi->table, Fi->key, where, &cats);
	G_debug(3, "%d categories selected from table <%s>", ncats, Fi->table);

	if (!column_names) {
	    db_close_database(driver);
	    db_shutdown_driver(driver);
	}
	else {
	    int icol, ncols;
	    const char *col_name;
            int len_all = 0;
            
	    db_set_string(&dbstring, Fi->table);
	    if (db_describe_table(driver, &dbstring, &Table) != DB_OK) {
		G_warning(_("Unable to describe table <%s>"), Fi->table);
		return -1;
	    }
	    
	    ncols = db_get_table_number_of_columns(Table);
	    columns = (char **) G_malloc((ncols + 1) * sizeof(char *));

            if (column_names[0] && strcmp(column_names[0], "*") == 0) {
                
                /* all columns */
                icol = 0;
                for (i = 0; i < ncols; i++) {
                    col_name = db_get_column_name(db_get_table_column(Table, i));
		    /* key column skipped */
                    if (strcmp(Fi->key, col_name) != 0)
			columns[icol++] = G_store(col_name);
                }
                columns[icol] = NULL;
            }
            else {
		int j;

		icol = 0;
		i = 0;
		while (column_names[i]) {
		    /* key column skipped */
                    if (strcmp(Fi->key, column_names[i]) != 0) {
			found = 0;
			for (j = 0; j < ncols; j++) {
			    col_name = db_get_column_name(db_get_table_column(Table, j));
			    if (strcmp(col_name, column_names[i]) == 0) {
				columns[icol++] = G_store(col_name);
				found = 1;
				break;
			    }
			}
			if (!found) {
			    G_warning(_("Column <%s> does not exist"),
				      column_names[i]);
			    G_important_message(_("Available columns:"));
			    for (j = 0; j < ncols; j++) {
				col_name = db_get_column_name(db_get_table_column(Table, j));
				G_important_message("%s", col_name);
			    }
			    G_warning(_("Export cancelled"));
			    db_close_database(driver);
			    db_shutdown_driver(driver);
			    return -1;
			}
		    }
		    i++;
                }
                columns[icol] = NULL;
            }

	    db_zero_string(&dbstring);
	    db_free_table(Table);
	    Table = NULL;
            
	    if (columns[0]) {
		/* selected columns only */
		i = 0;
		while (columns[i])
		    len_all += strlen(columns[i++]);
		
		coltypes = G_malloc(i * sizeof(int));
		
		all_columns = G_malloc(len_all + i + 2);

		i = 0;
		strcpy(all_columns, columns[0]);
		while (columns[i]) {
		    /* get column types */
		    coltypes[i] = db_column_Ctype(driver, Fi->table, columns[i]);
		    if (coltypes[i] < 0) {
			db_close_database(driver);
			db_shutdown_driver(driver);
			G_warning(_("Unknown type of column <%s>, export cancelled"),
				  columns[i]);
			return -1;
		    }
		    if (i > 0) {
			strcat(all_columns, ",");
			strcat(all_columns, columns[i]);
		    }
		    i++;
		}
	    }
	    else {
		/* no column or only key column selected */
		G_free(columns);
		columns = NULL;

		db_close_database(driver);
		db_shutdown_driver(driver);
	    }
	}
    }

    if (format == GV_ASCII_FORMAT_POINT && header) {

	/* print header */
	if (Map->head.with_z)
	    fprintf(ascii, "east%snorth%sheight%scat", fs, fs, fs);
	else
	    fprintf(ascii, "east%snorth%scat", fs, fs);
	if (columns) {
	    for (i = 0; columns[i]; i++) {
		if (db_select_value
		    (driver, Fi->table, Fi->key, cat,
		     columns[i], &value) < 0)
		    G_fatal_error(_("Unable to select record from table <%s> (key %s, column %s)"),
				  Fi->table, Fi->key, columns[i]);
		if (columns[i])
		    fprintf(ascii, "%s%s", fs, columns[i]);
		else
		    fprintf(ascii, "%s", columns[i]); /* can not happen */
	    }
	}
	fprintf(ascii, "%s", HOST_NEWLINE);
    }

    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();
    ACats = Vect_new_cats_struct();
    fcats = Vect_new_list();

    /* by default, read_next_line will NOT read Dead lines */
    /* but we can override that (in Level I only) by specifying */
    /* the type  -1, which means match all line types */

    Vect_rewind(Map);

    count = n_skipped = line = 0;
    while (TRUE) {
	ltype = Vect_read_next_line(Map, Points, Cats);
	if (ltype == -1 ) {      /* failure */
	    if (columns) {
		db_close_database(driver);
		db_shutdown_driver(driver);

                free_col_arrays(coltypes, all_columns,
                                column_names && strcmp(column_names[0], "*") == 0 ? columns : NULL);
	    }
	    
	    return -1;
	}

	if (ltype == -2)	{	/* EOF */
	    if (columns) {
		db_close_database(driver);
		db_shutdown_driver(driver);
                
                free_col_arrays(coltypes, all_columns,
                                column_names && strcmp(column_names[0], "*") == 0 ? columns : NULL);
	    }
	    break;
	}

	line++;

	if (!(ltype & type))
	    continue;

	if (format == GV_ASCII_FORMAT_POINT && !(ltype & GV_POINTS))
	    continue;

	found = get_cat(Cats, Clist, cats, ncats, field, &cat);

	if (!found && field > 0 && ltype == GV_BOUNDARY &&
	    type & GV_AREA && Vect_level(Map) > 1) {
	    Vect_get_line_areas(Map, line, &left, &right);
	    if (left < 0)
		left = Vect_get_isle_area(Map, abs(left));
	    if (left > 0) {
		Vect_get_area_cats(Map, left, ACats);
		found = get_cat(ACats, Clist, cats, ncats, field, &cat);
	    }
	    if (right < 0)
		right = Vect_get_isle_area(Map, abs(right));
	    if (!found && right > 0) {
		Vect_get_area_cats(Map, right, ACats);
		found = get_cat(ACats, Clist, cats, ncats, field, &cat);
	    }
	}
	
	if (!found) {
            if (Cats->n_cats < 1)
                n_skipped++;
            
	    continue;
	}

	if (ver < 5) {
	    Vect_cat_get(Cats, 1, &cat);
	}

	switch (ltype) {
	case GV_BOUNDARY:
	    if (ver == 5)
		ctype = 'B';
	    else
		ctype = 'A';
	    break;
	case GV_CENTROID:
	    if (ver < 5) {
		if (att != NULL) {
		    if (cat > 0) {
			G_rasprintf(&xstring, &xsize, "%.*f", dp, Points->x[0]);
			G_trim_decimal(xstring);
			G_rasprintf(&ystring, &ysize, "%.*f", dp, Points->y[0]);
			G_trim_decimal(ystring);
			fprintf(att, "A %s %s %d%s", xstring, ystring, cat, HOST_NEWLINE);
		    }
		}
		continue;
	    }
	    ctype = 'C';
	    break;
	case GV_LINE:
	    ctype = 'L';
	    break;
	case GV_POINT:
	    ctype = 'P';
	    break;
	case GV_FACE:
	    ctype = 'F';
	    break;
	case GV_KERNEL:
	    ctype = 'K';
	    break;
	default:
	    ctype = 'X';
	    G_warning(_("Unknown feature type %d"), (int)ltype);
	    break;
	}

	if (format == GV_ASCII_FORMAT_POINT) {
	    if (region_flag) {
		if ((window.east < Points->x[0]) ||
		    (window.west > Points->x[0]))
		    continue;
	    }
	    G_rasprintf(&xstring, &xsize, "%.*f", dp, Points->x[0]);
	    G_trim_decimal(xstring);

	    if (region_flag) {
		if ((window.north < Points->y[0]) ||
		    (window.south > Points->y[0]))
		    continue;
	    }
	    G_rasprintf(&ystring, &ysize, "%.*f", dp, Points->y[0]);
	    G_trim_decimal(ystring);

	    Vect_field_cat_get(Cats, field, fcats);

	    if (Map->head.with_z && ver == 5) {
		if (region_flag) {
		    if ((window.top < Points->z[0]) ||
			(window.bottom > Points->z[0]))
			continue;
		}
		G_rasprintf(&zstring, &zsize, "%.*f", dp, Points->z[0]);
		G_trim_decimal(zstring);
		fprintf(ascii, "%s%s%s%s%s", xstring, fs, ystring, fs,
			zstring);
	    }
	    else {
		fprintf(ascii, "%s%s%s", xstring, fs, ystring);
	    }

	    if (fcats->n_values > 0 && cat > -1) {
		if (fcats->n_values > 1) {
		    G_warning(_("Feature has more categories. Only one category (%d) "
				"is exported."), cat);
		}
		fprintf(ascii, "%s%d", fs, cat);
		
		/* print attributes */
		if (columns) {

		    G_rasprintf(&buf, &bufsize, "SELECT %s FROM %s WHERE %s = %d",
			    all_columns, Fi->table, Fi->key, cat);
		    G_debug(2, "SQL: %s", buf);
		    db_set_string(&dbstring, buf);

		    if (db_open_select_cursor
				    (driver, &dbstring, &cursor, DB_SEQUENTIAL) != DB_OK) {
			db_close_database(driver);
			db_shutdown_driver(driver);
			G_fatal_error(_("Cannot select attributes for cat = %d"),
			  cat);
		    }
		    if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK) {
			db_close_database(driver);
			db_shutdown_driver(driver);
			G_fatal_error(_("Unable to fetch data from table"));
		    }

		    Table = db_get_cursor_table(&cursor);


		    for (i = 0; columns[i]; i++) {
			Column = db_get_table_column(Table, i);
			Value = db_get_column_value(Column);

			if (db_test_value_isnull(Value)) {
			    fprintf(ascii, "%s", fs);
			}
			else {
			    switch(coltypes[i])
			    {
			    case DB_C_TYPE_INT: {
				fprintf(ascii, "%s%d", fs, db_get_value_int(Value));
				break;
			    }
			    case DB_C_TYPE_DOUBLE: {
				fprintf(ascii, "%s%.*f", fs, dp, db_get_value_double(Value));
				break;
			    }
			    case DB_C_TYPE_STRING: {
				fprintf(ascii, "%s%s", fs, db_get_value_string(Value));
				break;
			    }
			    case DB_C_TYPE_DATETIME: {
				break;
			    }
			    case -1:
				G_fatal_error(_("Column <%s> not found in table <%s>"),
					      columns[i], Fi->table);
			    default: G_fatal_error(_("Column <%s>: unsupported data type"),
						   columns[i]);
			    }
			}
		    }
		    db_close_cursor(&cursor);
		}
	    }

	    fprintf(ascii, "%s", HOST_NEWLINE);
	}
	else if (format == GV_ASCII_FORMAT_STD) {
	    /* FORMAT_STANDARD */
	    if (ver == 5 && Cats->n_cats > 0)
		fprintf(ascii, "%c  %d %d%s", ctype, Points->n_points,
			Cats->n_cats, HOST_NEWLINE);
	    else
              fprintf(ascii, "%c  %d%s", ctype, Points->n_points, HOST_NEWLINE);

	    xptr = Points->x;
	    yptr = Points->y;
	    zptr = Points->z;

	    while (Points->n_points--) {

		G_rasprintf(&xstring, &xsize, "%.*f", dp, *xptr++);
		G_trim_decimal(xstring);
		G_rasprintf(&ystring, &ysize, "%.*f", dp, *yptr++);
		G_trim_decimal(ystring);

		if (ver == 5) {
		    if (Map->head.with_z) {
			G_rasprintf(&zstring, &zsize, "%.*f", dp, *zptr++);
			G_trim_decimal(zstring);
			fprintf(ascii, " %-12s %-12s %-12s%s", xstring,
				ystring, zstring, HOST_NEWLINE);
		    }
		    else {
                      fprintf(ascii, " %-12s %-12s%s", xstring, ystring, HOST_NEWLINE);
		    }
		}		/*Version 4 */
		else {
                    fprintf(ascii, " %-12s %-12s%s", ystring, xstring, HOST_NEWLINE);
		}
	    }

	    if (ver == 5) {
		for (i = 0; i < Cats->n_cats; i++) {
		    fprintf(ascii, " %-5d %-10d%s", Cats->field[i],
			    Cats->cat[i], HOST_NEWLINE);
		}
	    }
	    else {
		if (cat > -1) {
		    if (ltype == GV_POINT) {
			G_rasprintf(&xstring, &xsize, "%.*f", dp, Points->x[0]);
			G_trim_decimal(xstring);
			G_rasprintf(&ystring, &ysize, "%.*f", dp, Points->y[0]);
			G_trim_decimal(ystring);
			fprintf(att, "P %s %s %d%s", xstring, ystring, cat, HOST_NEWLINE);
		    }
		    else {
			x = (Points->x[1] + Points->x[0]) / 2;
			y = (Points->y[1] + Points->y[0]) / 2;

			G_rasprintf(&xstring, &xsize, "%.*f", dp, x);
			G_trim_decimal(xstring);
			G_rasprintf(&ystring, &ysize, "%.*f", dp, y);
			G_trim_decimal(ystring);
			fprintf(att, "L %s %s %d%s", xstring, ystring, cat, HOST_NEWLINE);
		    }
		}
	    }
	}
	else if (format == GV_ASCII_FORMAT_WKT) {
	    if (ltype & (GV_BOUNDARY | GV_CENTROID | GV_FACE | GV_KERNEL))
		continue;
	    /* Well-Known Text */
	    Vect_sfa_line_astext(Points, ltype, Vect_is_3d(Map), dp, ascii);
	    count++;
	}
	else {
	    G_fatal_error(_("Unknown format"));
	}
	count++;
    }

    if (format == GV_ASCII_FORMAT_WKT) {
	/* process areas - topology required */
	int i, area, nareas, isle, nisles;

	if (Vect_level(Map) < 2) {
	    G_warning(_("Topology not available, unable to process areas"));
	    nareas = 0;
	}
	else {
	    nareas = Vect_get_num_areas(Map);
	}
	for (area = 1; area <= nareas; area++) {
	    if (!Vect_area_alive(Map, area)) /* skip dead areas */
		continue;
	    if (Vect_get_area_cat(Map, area, field) < 0)
		continue;
	    /* get boundary -> linearring */
	    if (Vect_get_area_points(Map, area, Points) < 0) {
		G_warning(_("Unable to get boundary of area id %d"), area);
		continue;
	    }
	    fprintf(ascii, "POLYGON(");
	    /* write outter ring */
	    Vect_sfa_line_astext(Points, GV_BOUNDARY, 0, dp, ascii); /* boundary is always 2D */
	    /* get isles (holes) -> inner rings */
	    nisles = Vect_get_area_num_isles(Map, area);
	    for (i = 0; i < nisles; i++) {
		/* get isle boundary -> linearring */
		isle = Vect_get_area_isle(Map, area, i);
		if (Vect_get_isle_points(Map, isle, Points) < 0) {
		    G_warning(_("Unable to get boundary of isle id %d (area id %d)"), isle, area);
		    continue;
		}
		fprintf(ascii, ", ");
		/* write inner ring */
		Vect_sfa_line_astext(Points, GV_BOUNDARY, 0, dp, ascii); /* boundary is always 2D */
	    }
	    fprintf(ascii, ")%s", HOST_NEWLINE);
	    
	    count++;
	}
    }

    if (n_skipped > 0)
        G_important_message(_("%d features without category skipped. To export also "
                              "features without category use '%s=-1'."), n_skipped, "layer");
    
    Vect_destroy_line_struct(Points);
    Vect_destroy_cats_struct(Cats);
    Vect_destroy_cats_struct(ACats);
    
    return count;
}
int
main (int argc, char **argv, char **envp)
{
  int i;
  cat_list *flat_cl;
  cat_list *cl; /* hierarchical list */
  const struct cat *cur_cat;

  SETUP_LOGGER ("/dev/stderr", errtostr);

  /* test for emptiness */
  update_cat_list ("delete from " TABLE_CATEGORY_LIST ";"
		   "delete from " TABLE_CATEGORY_STRUCTURE ";");
  assert (load_flat_cat_list (&flat_cl) == 0);
  assert (!next (flat_cl));
  assert (!prev (flat_cl));
  assert (!go_sub (flat_cl));
  assert (!go_sup (flat_cl));
  assert (get_cat (flat_cl) == NULL);
  assert (reset (flat_cl) == 0);

  assert (load_cat_list (&cl) == 0);
  assert (!next (cl));
  assert (!prev (cl));
  assert (!go_sub (cl));
  assert (!go_sup (cl));
  assert (get_cat (cl) == NULL);
  assert (reset (cl) == 0);

  /* test for update not affecting existing list */
  update_cat_list ("insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (0, 'NULL');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (1, 'Restaurants');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (2, 'Schools');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (3, 'Games');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (4, 'Kindergarden');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (5, 'Elementary School');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (6, 'Private School');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (7, 'Public School');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (8, 'Logic Games');"
		   "insert into " TABLE_CATEGORY_LIST
		   " (" COLUMN_CAT_ID ", " COLUMN_CAT_NAME ")"
		   " values (9, 'Card Games');"

		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (2, 6);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (2, 7);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (6, 4);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (6, 5);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (7, 4);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (7, 5);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (3, 8);"
		   "insert into " TABLE_CATEGORY_STRUCTURE
		   " (" COLUMN_CAT_ID ", " COLUMN_SUBCAT_ID ")"
		   " values (3, 9);");

  /* not affected list */
  assert (!next (cl));
  assert (!prev (cl));
  assert (!go_sub (cl));
  assert (!go_sup (cl));
  assert (get_cat (cl) == NULL);

  /* affected list */
  assert (reset (flat_cl) == 0);
  assert (!prev (flat_cl));
  assert (!go_sub (flat_cl));
  assert (!go_sup (flat_cl));
  assert (get_cat (flat_cl) == NULL);
  char *expected_categories[] = {
    "NULL", "Restaurants", "Schools", "Games", "Kindergarden",
    "Elementary School", "Private School", "Public School", "Logic Games",
    "Card Games",
  };
  for (i = 0; i < 10; i++)
    {
      assert (next (flat_cl) == -1);
      assert (!go_sub (flat_cl));
      assert (!go_sup (flat_cl));
      cur_cat = get_cat (flat_cl);
      assert (cur_cat != NULL);
      assert (cur_cat->id == i);
      assert (strcmp (cur_cat->name, expected_categories[i]) == 0);
    }

  /* cur_cat stays at the last reading when next cannot advance to the next cat */
  assert (!next (flat_cl));
  --i;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  for (--i; i >= 0; i--)
    {
      assert (prev (flat_cl) == -1);
      assert (!go_sub (flat_cl));
      assert (!go_sup (flat_cl));
      cur_cat = get_cat (flat_cl);
      assert (cur_cat != NULL);
      assert (cur_cat->id == i);
      assert (strcmp (cur_cat->name, expected_categories[i]) == 0);
    }

  /* cur_cat stays at the last reading when prev cannot advance to the prev cat */
  assert (!prev (flat_cl));
  ++i;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* Now try prev in the middle of the list */
  assert (reset (flat_cl) == 0);
  assert (next (flat_cl) == -1);
  assert (next (flat_cl) == -1);
  assert (next (flat_cl) == -1);
  assert (next (flat_cl) == -1);
  assert (next (flat_cl) == -1);
  assert (next (flat_cl) == -1);
  assert (prev (flat_cl) == -1);
  assert (prev (flat_cl) == -1);
  i = 3;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* Try the hierarchy iteration out */
  assert (reset (cl) == 0);
  assert (!prev (cl));
  assert (!go_sub (cl));
  assert (!go_sup (cl));
  assert (get_cat (cl) == NULL);

  for (i = 0; i < 4; i++)
    {
      assert (next (cl) == -1);
      cur_cat = get_cat (cl);
      assert (cur_cat->id == i);
      assert (strcmp (cur_cat->name, expected_categories[i]) == 0);
    }

  assert (next (cl) == 0);
  cur_cat = get_cat (cl);
  i = 3;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* Go back to schools */
  assert (prev (cl) == -1);
  cur_cat = get_cat (cl);
  i = 2;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go down to private schools */
  assert (!go_sup (cl));
  assert (go_sub (cl) == -1);
  cur_cat = get_cat (cl);
  i = 6;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go next to public schools */
  assert (!prev (cl));
  assert (next (cl) == -1);
  cur_cat = get_cat (cl);
  i = 7;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go down to kindergarden */
  assert (go_sub (cl) == -1);
  cur_cat = get_cat (cl);
  i = 4;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go up to public schools */
  assert (!prev (cl));
  assert (!go_sub (cl));
  assert (go_sup (cl) == -1);
  cur_cat = get_cat (cl);
  i = 7;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go back to private schools */
  assert (!next (cl));
  assert (prev (cl) == -1);
  cur_cat = get_cat (cl);
  i = 6;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go down to kindergarden */
  assert (!prev (cl));
  assert (go_sub (cl) == -1);
  cur_cat = get_cat (cl);
  i = 4;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go next to elementary school */
  assert (!prev (cl));
  assert (!go_sub (cl));
  assert (next (cl) == -1);
  assert (!next (cl));
  assert (!go_sub (cl));
  cur_cat = get_cat (cl);
  i = 5;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go up to private shools */
  assert (go_sup (cl) == -1);
  assert (!prev (cl));
  cur_cat = get_cat (cl);
  i = 6;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go up to shools */
  assert (go_sup (cl) == -1);
  cur_cat = get_cat (cl);
  i = 2;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  /* go next to games */
  assert (!go_sup (cl));
  assert (next (cl) == -1);
  assert (!next (cl));
  cur_cat = get_cat (cl);
  i = 3;
  assert (cur_cat->id == i);
  assert (strcmp (cur_cat->name, expected_categories[i]) == 0);

  destroy_cat_list (&flat_cl);
  destroy_cat_list (&cl);

  exit (EXIT_SUCCESS);
}