int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
{
	int rc = -1;
	int flags = 0;
	pid_t tid = -1;
	struct libmnt_parser pa = { .line = 0 };

	assert(tb);
	assert(f);
	assert(filename);

	DBG(TAB, ul_debugobj(tb, "%s: start parsing [entries=%d, filter=%s]",
				filename, mnt_table_get_nents(tb),
				tb->fltrcb ? "yes" : "not"));

	pa.filename = filename;
	pa.f = f;

	/* necessary for /proc/mounts only, the /proc/self/mountinfo
	 * parser sets the flag properly
	 */
	if (tb->fmt == MNT_FMT_SWAPS)
		flags = MNT_FS_SWAP;
	else if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0)
		flags = MNT_FS_KERNEL;

	do {
		struct libmnt_fs *fs;

		if (feof(f)) {
			DBG(TAB, ul_debugobj(tb, "end-of-file"));
			break;
		}
		fs = mnt_new_fs();
		if (!fs)
			goto err;

		/* parse */
		rc = mnt_table_parse_next(&pa, tb, fs);

		if (rc == 0 && tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data))
			rc = 1;	/* filtered out by callback... */

		/* add to the table */
		if (rc == 0) {
			rc = mnt_table_add_fs(tb, fs);
			fs->flags |= flags;

			if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) {
				rc = kernel_fs_postparse(tb, fs, &tid, filename);
				if (rc)
					mnt_table_remove_fs(tb, fs);
			}
		}

		/* remove reference (or deallocate on error) */
		mnt_unref_fs(fs);

		/* recoverable error */
		if (rc > 0) {
			DBG(TAB, ul_debugobj(tb, "recoverable error (continue)"));
			continue;
		}

		/* fatal errors */
		if (rc < 0 && !feof(f)) {
			DBG(TAB, ul_debugobj(tb, "fatal error"));
			goto err;
		}
	} while (1);

	DBG(TAB, ul_debugobj(tb, "%s: stop parsing (%d entries)",
				filename, mnt_table_get_nents(tb)));
	parser_cleanup(&pa);
	return 0;
err:
	DBG(TAB, ul_debugobj(tb, "%s: parse error (rc=%d)", filename, rc));
	parser_cleanup(&pa);
	return rc;
}
int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
{
	int rc = -1;
	int flags = 0;
	pid_t tid = -1;
	struct libmnt_parser pa = { .line = 0 };

	assert(tb);
	assert(f);
	assert(filename);

	DBG(TAB, ul_debugobj(tb, "%s: start parsing [entries=%d, filter=%s]",
				filename, mnt_table_get_nents(tb),
				tb->fltrcb ? "yes" : "not"));

	pa.filename = filename;
	pa.f = f;

	 
	if (tb->fmt == MNT_FMT_SWAPS)
		flags = MNT_FS_SWAP;
	else if (filename && strcmp(filename, _PATH_PROC_MOUNTS) == 0)
		flags = MNT_FS_KERNEL;

	do {
		struct libmnt_fs *fs;

		if (feof(f)) {
			DBG(TAB, ul_debugobj(tb, "end-of-file"));
			break;
		}
		fs = mnt_new_fs();
		if (!fs)
			goto err;

		 
		rc = mnt_table_parse_next(&pa, tb, fs);

		if (rc == 0 && tb->fltrcb && tb->fltrcb(fs, tb->fltrcb_data))
			rc = 1;	 

		 
		if (rc == 0) {
			rc = mnt_table_add_fs(tb, fs);
			fs->flags |= flags;

			if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) {
				rc = kernel_fs_postparse(tb, fs, &tid, filename);
				if (rc)
					mnt_table_remove_fs(tb, fs);
			}
		}

		 
		mnt_unref_fs(fs);

		 
		if (rc > 0) {
			DBG(TAB, ul_debugobj(tb, "recoverable error (continue)"));
			continue;
		}

		 
		if (rc < 0 && !feof(f)) {
			DBG(TAB, ul_debugobj(tb, "fatal error"));
			goto err;
		}
	} while (1);

	DBG(TAB, ul_debugobj(tb, "%s: stop parsing (%d entries)",
				filename, mnt_table_get_nents(tb)));
	parser_cleanup(&pa);
	return 0;
err:
	DBG(TAB, ul_debugobj(tb, "%s: parse error (rc=%d)", filename, rc));
	parser_cleanup(&pa);
	return rc;
}