Example #1
0
char *aobjnam(struct obj *otmp, char *verb)
{
    char *bp = xname(otmp);
    char prefix[PREFIX];

    if(otmp->quan != 1) {
        sprintf(prefix, "%d ", otmp->quan);
        bp = strprepend(bp, prefix);
    }

    if(verb != NULL) {
        /* Verb is given in plural (i.e., without trailing 's') */
        strcat(bp, " ");

        if(otmp->quan != 1) {
            strcat(bp, verb);
        }
        else if(strcmp(verb, "are") == 0) {
            strcat(bp, "is");
        }
        else {
            strcat(bp, verb);
            strcat(bp, "s");
        }
    }

    return bp;
}
Example #2
0
repository_t *local_get_repository(driver_t * self, char *supfile,
				   list_t * repositories_hierarchy)
{
	FILE *file;
	char *line, *path, *name, *extension;
	repository_t *repository;
	size_t n;
	int nread;

	assert(supfile);
	if (!(extension = rindex(supfile, '.')) || strcmp(extension, ".local"))
		return NULL;

	supfile = xstrdup_printf("/etc/ports/%s", supfile);

	file = fopen(supfile, "r");
	if (file == NULL)
		return NULL;

	line = NULL;
	repository = NULL;

	while ((nread = getline(&line, &n, file)) >= 0) {
		*(line + strlen(line) - 1) = 0;
		strtrim(line);
		if (line[0] == '#' || strlen(line) == 0 ||
		    (!strstr(line, "PATH") && strchr(line, '=')))
			continue;
		path = strtrim(xstrdup(strchr(line, '=') + 1));
		name = xstrdup(strrchr(supfile, '/'));
		*(strrchr(name, '.')) = 0;
		strprepend(&name, "local");
		repository = repository_new(name, path, supfile, self,
					    repositories_hierarchy);
		break;
	}
	free(line);
	fclose(file);
	return repository;
}
Example #3
0
unsigned char *ramdisk_load (const char *filename, int32_t *outlen)
{
    ramdisk_t *ramfile = ramdisk_data;
    unsigned char *out;
    char *alt_filename;

    alt_filename = 0;

    /*
     * If the file is on disk and is newer than the program, use that in
     * preference.
     */
    if (file_exists(filename)) {
        if (strstr(filename, "data/")) {
            if (file_exists_and_is_newer_than(filename,
                                              EXEC_FULL_PATH_AND_NAME)) {
                out = file_read_if_exists(filename, outlen);
                if (out) {
                    DBG("Locdisk %s (newer than exec)", filename);
                    return (out);
                }
            }

            if (file_exists_and_is_newer_than(filename, ".o/ramdisk_data.o")) {
                out = file_read_if_exists(filename, outlen);
                if (out) {
                    DBG("Locdisk %s (newer than build)", filename);
                    return (out);
                }
            }

            if (file_exists_and_is_newer_than(filename, "src/.o/ramdisk_data.o")) {
                out = file_read_if_exists(filename, outlen);
                if (out) {
                    DBG("Locdisk %s (newer than src build)", filename);
                    return (out);
                }
            }
        } else {
            out = file_read_if_exists(filename, outlen);
            if (out) {
                DBG("Locdisk %s (exists locally)", filename);
                return (out);
            }
        }
    }

    if (EXEC_DIR) {
        alt_filename = strprepend(filename, EXEC_DIR);

        if (file_exists(alt_filename)) {
            if (file_exists_and_is_newer_than(alt_filename,
                                              EXEC_FULL_PATH_AND_NAME)) {
                out = file_read_if_exists(alt_filename, outlen);
                if (out) {
                    DBG("Locdisk %s", filename);
                    myfree(alt_filename);

                    return (out);
                }
            }

            if (file_exists_and_is_newer_than(alt_filename,
                                            ".o/ramdisk_data.o")) {
                out = file_read_if_exists(alt_filename, outlen);
                if (out) {
                    DBG("Locdisk %s", filename);
                    myfree(alt_filename);

                    return (out);
                }
            }

            if (file_exists_and_is_newer_than(alt_filename,
                                            "src/.o/ramdisk_data.o")) {
                out = file_read_if_exists(alt_filename, outlen);
                if (out) {
                    DBG("Locdisk %s", filename);
                    myfree(alt_filename);

                    return (out);
                }
            }
        }
    }

    while (ramfile->filename) {
        if (strcmp(ramfile->filename, filename)) {
            ramfile++;
            continue;
        }

        if (outlen) {
            *outlen = (int)ramfile->len;
        }

        DBG("Ramdisk %s, %d bytes", filename, ramfile->len);

        if (alt_filename) {
            myfree(alt_filename);
        }

        uint8_t *copy = (TYPEOF(copy))
                        mymalloc((int)ramfile->len + 1, "ramdisk load");
        if (!copy) {
            DBG("no memory for loading ramdisk copy, %s", filename);
            return (0);
        }

        memcpy(copy, (unsigned char*)ramfile->data, (int)ramfile->len);
        *(copy + (int)ramfile->len) = 0;

        return (copy);

#ifdef USE_ZLIB
        int32_t err;

        out = mymalloc(ramfile->orig_len, "RAMDISK scratchpad");
        if (!out) {
            ERR("no memory for ramdisk, %s", filename);
        }

        outlenl = ramfile->orig_len;
        err = uncompress(out, &outlenl,
                         (unsigned char *)ramfile->data,
                         ramfile->len);
        if (err) {
            ERR("file failed to decompress from ramdisk, %s, "
                "orig len %d, compressed len %d, err %d",
                filename, ramfile->orig_len, ramfile->len, err);
        }

        if (outlen) {
            *outlen = (int)outlenl;
        }
#endif

#ifdef USE_MINIZ
        int32_t err;

        out = (TYPEOF(out)) mymalloc(ramfile->orig_len, "RAMDISK scratchpad");
        if (!out) {
            ERR("no memory for ramdisk, %s", filename);
        }

        outlenl = ramfile->orig_len;
        err = mz_uncompress(out, &outlenl,
                            (unsigned char *)ramfile->data,
                            ramfile->len);
        if (err) {
            ERR("file failed to decompress from ramdisk, %s, err %d",
                filename, err);
        }

        if (outlen) {
            *outlen = (int32_t)outlenl;
        }
#endif

#ifdef USE_STB_IMAGE
        out = (TYPEOF(out))
            stbi_zlib_decode_malloc((const char *)ramfile->data,
                                    ramfile->len, &outlenl);
        if (!out) {
            ERR("file failed to decompress from ramdisk, %s", filename);
        }

        if (outlen) {
            *outlen = (int)outlenl;
        }
#endif

        return (out);
    }

    /*
     * Fallback to the disk.
     */
    out = file_read_if_exists(filename, outlen);
    if (out) {
        DBG("Locdisk %s", filename);

        if (alt_filename) {
            myfree(alt_filename);
        }

        return (out);
    }

    out = file_read_if_exists(alt_filename, outlen);
    if (out) {
        DBG("Locdisk %s", filename);

        if (alt_filename) {
            myfree(alt_filename);
        }

        return (out);
    }

    if (alt_filename) {
        myfree(alt_filename);
    }

    alt_filename = mybasename(filename, "strip dir");

    out = file_read_if_exists(alt_filename, outlen);
    if (out) {
        DBG("Locdisk %s", alt_filename);

        if (alt_filename) {
            myfree(alt_filename);
        }

        return (out);
    }

    /*
     * Fail. Caller should whinge.
     *
    char *popup_str = dynprintf("Filename was not found on ramdisk or "
                                "on the local disk, %s", filename);

    MSG_BOX("%s", popup_str);
    myfree(popup_str);
     */
    LOG("File not found \"%s\"", filename);

    if (alt_filename) {
        myfree(alt_filename);
    }

    return (0);
}
Example #4
0
char *doname(struct obj *obj)
{
    char prefix[PREFIX];
    char *bp = xname(obj);

    if(obj->quan != 1) {
        sprintf(prefix, "%d ", obj->quan);
    }
    else {
        strcpy(prefix, "a ");
    }

    switch(obj->olet) {
    case AMULET_SYM:
        if(strncmp(bp, "cheap ", 6) != 0) {
            strcpy(prefix, "the ");
        }

        break;
    case ARMOR_SYM:
        if((obj->owornmask & W_ARMOR) != 0) {
            strcat(bp, " (being worn)");
        }

        if(obj->known != 0) {
            strcat(prefix, sitoa((obj->spe - 10) + objects[obj->otyp].a_ac));
            strcat(prefix, " ");
        }
        
        break;
    case WEAPON_SYM:
        if(obj->known != 0) {
            strcat(prefix, sitoa(obj->spe));
            strcat(prefix, " ");
        }

        break;
    case WAND_SYM:
        if(obj->known) {
            sprintf(eos(bp), " (%d)", obj->spe);
        }

        break;
    case RING_SYM:
        if((obj->owornmask & W_RINGR) != 0) {
            strcat(bp, " (on right hand)");
        }

        if((obj->owornmask & W_RINGL) != 0) {
            strcat(bp, " (on left hand)");
        }
        
        if((obj->known != 0) && ((objects[obj->otyp].bits & SPEC) != 0)) {
            strcat(prefix, sitoa(obj->spe));
            strcat(prefix, " ");
        }

        break;
    }

    if((obj->owornmask & W_WEP) != 0) {
        strcat(bp, " (weapon in hand)");
    }

    if(obj->unpaid != 0) {
        strcat(bp, " (unpaid)");
    }

    if((strcmp(prefix, "a ") == 0) && (index(vowels, *bp) != 0)) {
        strcpy(prefix, "an ");
    }

    bp = strprepend(bp, prefix);
    
    return bp;
}
Example #5
0
int main(int argc, char **argv)
{
	unsigned i;
	conf_t *conf;
	dict_t *repositories;
	dict_t *ports_dict;
	dict_t *not_founds;
	list_t *ports_list;
	list_t *packages;
	list_t *list;
	list_t *drivers;
	struct arguments arguments;

	arguments.action = 0;
	arguments.no_favourite_repositories = 0;
	arguments.no_locked_versions = 0;
	arguments.no_aliases = 0;
	arguments.no_colors = 0;
	arguments.no_deps = 1;
	arguments.all = 0;
	arguments.fetch_only = 0;
	arguments.no_repositories_hierarchy = 0;
	arguments.tree = 0;
	arguments.verbose = 0;
	arguments.rebuild_cache = 0;
	arguments.args = list_new();

	if (argc < 2) {
		char *fake_arg[2];
		fake_arg[0] = "ilenia";
		fake_arg[1] = "--help";
		argp_parse(&argp, 2, fake_arg, 0, 0, &arguments);
	}

	argp_parse(&argp, argc, argv, 0, 0, &arguments);

	conf = conf_init();

	if (arguments.no_repositories_hierarchy) {
		list_free(conf->repositories_hierarchy, free);
		conf->repositories_hierarchy = list_new();
	}

	if (arguments.no_favourite_repositories) {
		dict_free(conf->favourite_repositories, NULL);
		conf->favourite_repositories = dict_new();
	}

	if (arguments.no_locked_versions) {
		dict_free(conf->locked_versions, free);
		conf->locked_versions = dict_new();
	}

	if (arguments.no_aliases) {
		aliases_free(conf->aliases);
		conf->aliases = dict_new();
	}

	drivers = drivers_list_init();
	repositories = repositories_dict_init(drivers,
					      conf->repositories_hierarchy);
	if (!conf->enable_colors || arguments.no_colors)
		cprintf = uncoloredprintf;
	else
		cprintf = coloredprintf;

	if (arguments.rebuild_cache)
		cache_build(repositories);
	packages = packages_list_init();
	if ((ports_list = ports_list_init(repositories)) == NULL) {
		list_free(arguments.args, NULL);
		dict_free(repositories, repository_free);
		list_free(packages, port_free);
		list_free(drivers, free);
		dict_free(conf->favourite_repositories, free);
		conf->favourite_repositories = dict_new();
		conf_free(conf);
		return 1;
	}

	conf_reparse(conf, ports_list);
	ports_dict = ports_dict_init(ports_list, packages, conf);
	not_founds = dict_new();

	if (!arguments.verbose)
		arguments.verbose = conf->verbose;

	switch (arguments.action) {
	case ACT_LIST:
		if (!arguments.args->length) {
			list_dump(ports_list, port_dump);
			break;
		}
		for (i = 0; i < arguments.args->length; i++) {
			if (dict_get(repositories, list_get(arguments.args, i))
			    == NULL) {
				warning("repository %s not found!",
					list_get(arguments.args, i));
				continue;
			}
			list = list_query(ports_list,
					  port_query_by_repository,
					  list_get(arguments.args, i));
			list_dump(list, port_dump);
			list_free(list, NULL);
		}
		break;
	case ACT_SEARCH:
		if (!arguments.args->length) {
			warning("action --search (-s) requires at least an "
				"argument!");
			break;
		}
		for (i = 0; i < arguments.args->length; i++) {
			list = list_query(ports_list, port_query_by_name,
					  list_get(arguments.args, i));
			if (arguments.verbose)
				list_dump(list, port_info_dump);
			else
				list_dump(list, port_dump);
			if (list->length == 0)
				printf("%s not found!\n",
				       (char *)list_get(arguments.args, i));
			list_free(list, NULL);
		}
		break;
	case ACT_SEARCHDESC:
		if (!arguments.args->length) {
			warning("action --search-desc requires at least an "
				"argument!");
			break;
		}
		for (i = 0; i < arguments.args->length; i++) {
			char *key = xstrdup(list_get(arguments.args, i));
			if (isalpha(*key) && isalpha(*(key + strlen(key) - 1))) {
				strprepend(&key, "*");
				strappend(&key, "*");
			}
			list = list_query(ports_list, port_query_by_description,
					  key);
			if (arguments.verbose)
				list_dump(list, port_info_dump);
			else
				list_dump(list, port_dump);
			list_free(list, NULL);
			free(key);
		}
		break;
	case ACT_DIFF:
		port_show_diffs(ports_dict, packages);
		break;
	case ACT_UPDATED:
		port_show_outdated(ports_dict, packages);
		break;
	case ACT_DEPENDENCIES:
		if (!arguments.args->length) {
			warning("action --dependencies (-D) requires at "
				"least an argument!");
			break;
		}
		//for (i = 0; i < arguments.args->length; i++)
		dependencies_dump(arguments.args,
				  ports_dict, conf->aliases,
				  not_founds,
				  arguments.tree, arguments.verbose);
		break;
	case ACT_DEPENDENTS:
		if (!arguments.args->length) {
			warning("action --dependents (-T) requires at "
				"least an argument!");
			break;
		}
		for (i = 0; i < arguments.args->length; i++)
			dependents_dump(list_get(arguments.args, i),
					ports_dict, conf->aliases,
					arguments.tree, arguments.verbose,
					arguments.all);
		break;
	case ACT_INFO:
		if (!arguments.args->length) {
			warning("action --info requires at "
				"least an argument!");
			break;
		}
		for (i = 0; i < arguments.args->length; i++)
			info_dump(list_get(arguments.args, i), ports_dict);
		break;
	case ACT_README:
		if (!arguments.args->length) {
			warning("action --show-readme requires at "
				"least an argument!");
			break;
		}
		for (i = 0; i < arguments.args->length; i++)
			readme_dump(list_get(arguments.args, i), ports_dict);
		break;
	case ACT_REPOSITORY_LIST:
		repositories_dict_dump(repositories);
		break;
	case ACT_UPDATE:
		if (arguments.args->length)
			repositories_dict_update(repositories, arguments.args);
		else
			repositories_dict_update_all(repositories);
		break;
	case ACT_REMOVE:
		if (!arguments.args->length) {
			warning("action --remove (-R) requires at "
				"least an argument!");
			break;
		}
		remove_packages(arguments.args, packages, ports_dict, conf,
				arguments.all);
		break;
	case ACT_UPDATE_PKG:
		if (!arguments.args->length) {
			update_system(ports_dict,
				      conf->aliases, arguments.fetch_only,
				      conf->ask_for_update,
				      conf->not_found_policy);
			break;
		}
		update_package(arguments.args, ports_dict, conf,
			       arguments.fetch_only);
		break;
	default:
		if (!arguments.rebuild_cache)
			warning("What can I do for you?");
	}

	list_free(arguments.args, NULL);
	dict_free(repositories, repository_free);
	list_free(ports_list, port_free);
	list_free(packages, port_free);
	dict_free(ports_dict, NULL);
	list_free(drivers, free);
	conf_free(conf);
	dict_free(not_founds, port_free);

	return 0;
}
Example #6
0
/* Expands macros in the *format string advancing the pointer as it goes.  The
 * opt represents conditional expression state, should be zero for non-recursive
 * calls.  Returns newly allocated string, which should be freed by the
 * caller. */
static char *
parse_view_macros(FileView *view, const char **format, const char macros[],
		int opt)
{
	const dir_entry_t *const entry = &view->dir_entry[view->list_pos];
	char *result = strdup("");
	size_t len = 0;
	char c;
	int nexpansions = 0;

	while((c = **format) != '\0')
	{
		size_t width = 0;
		int left_align = 0;
		char buf[PATH_MAX];
		const char *const next = ++*format;
		int skip, ok;

		if(c != '%' || (!char_is_one_of(macros, *next) && !isdigit(*next)))
		{
			if(strappendch(&result, &len, c) != 0)
			{
				break;
			}
			continue;
		}

		if(*next == '-')
		{
			left_align = 1;
			++*format;
		}

		while(isdigit(**format))
		{
			width = width*10 + *(*format)++ - '0';
		}
		c = *(*format)++;

		skip = 0;
		ok = 1;
		switch(c)
		{
			case 't':
				format_entry_name(entry, sizeof(buf), buf);
				break;
			case 'A':
#ifndef _WIN32
				get_perm_string(buf, sizeof(buf), entry->mode);
#else
				snprintf(buf, sizeof(buf), "%s", attr_str_long(entry->attrs));
#endif
				break;
			case 'u':
				get_uid_string(entry, 0, sizeof(buf), buf);
				break;
			case 'g':
				get_gid_string(entry, 0, sizeof(buf), buf);
				break;
			case 's':
				friendly_size_notation(entry->size, sizeof(buf), buf);
				break;
			case 'E':
				{
					uint64_t size = 0;
					if(view->selected_files > 0)
					{
						int i;
						for(i = 0; i < view->list_rows; i++)
						{
							if(view->dir_entry[i].selected)
							{
								size += get_file_size_by_entry(view, i);
							}
						}
					}
					/* Make exception for VISUAL_MODE, since it can contain empty
					 * selection when cursor is on ../ directory. */
					else if(!vle_mode_is(VISUAL_MODE))
					{
						size = get_file_size_by_entry(view, view->list_pos);
					}
					friendly_size_notation(size, sizeof(buf), buf);
				}
				break;
			case 'd':
				{
					struct tm *tm_ptr = localtime(&entry->mtime);
					strftime(buf, sizeof(buf), cfg.time_format, tm_ptr);
				}
				break;
			case '-':
				skip = expand_num(buf, sizeof(buf), view->filtered);
				break;
			case 'l':
				skip = expand_num(buf, sizeof(buf), view->list_pos + 1);
				break;
			case 'L':
				skip = expand_num(buf, sizeof(buf), view->list_rows + view->filtered);
				break;
			case 'S':
				skip = expand_num(buf, sizeof(buf), view->list_rows);
				break;
			case '%':
				snprintf(buf, sizeof(buf), "%%");
				break;
			case '[':
				{
					char *const opt_str = parse_view_macros(view, format, macros, 1);
					copy_str(buf, sizeof(buf), opt_str);
					free(opt_str);
					break;
				}
			case ']':
				if(opt)
				{
					if(nexpansions == 0)
					{
						replace_string(&result, "");
					}
					return result;
				}
				else
				{
					LOG_INFO_MSG("Unmatched %]", c);
					ok = 0;
				}
				break;

			default:
				LOG_INFO_MSG("Unexpected %%-sequence: %%%c", c);
				ok = 0;
				break;
		}

		if(!ok)
		{
			*format = next;
			if(strappendch(&result, &len, '%') != 0)
			{
				break;
			}
			continue;
		}

		check_expanded_str(buf, skip, &nexpansions);
		stralign(buf, width, ' ', left_align);

		if(strappend(&result, &len, buf) != 0)
		{
			break;
		}
	}

	/* Unmatched %[. */
	if(opt)
	{
		(void)strprepend(&result, &len, "%[");
	}

	return result;
}
Example #7
0
/* Expands macros in the *format string advancing the pointer as it goes.  The
 * opt represents conditional expression state, should be zero for non-recursive
 * calls.  Returns newly allocated string, which should be freed by the
 * caller. */
static LineWithAttrs
parse_view_macros(view_t *view, const char **format, const char macros[],
		int opt)
{
	const dir_entry_t *const curr = get_current_entry(view);
	LineWithAttrs result = { .line = strdup(""), .attrs = strdup("") };
	char c;
	int nexpansions = 0;
	int has_expander = 0;

	if(curr == NULL)
	{
		return result;
	}

	while((c = **format) != '\0')
	{
		size_t width = 0;
		int left_align = 0;
		char buf[PATH_MAX + 1];
		const char *const next = ++*format;
		int skip, ok;

		if(c != '%' ||
				(!char_is_one_of(macros, *next) && !isdigit(*next) &&
				 (*next != '=' || has_expander)))
		{
			if(strappendch(&result.line, &result.line_len, c) != 0)
			{
				break;
			}
			continue;
		}

		if(*next == '=')
		{
			(void)sync_attrs(&result, 0);

			if(strappend(&result.line, &result.line_len, "%=") != 0 ||
					strappendch(&result.attrs, &result.attrs_len, '=') != 0)
			{
				break;
			}
			++*format;
			has_expander = 1;
			continue;
		}

		if(*next == '-')
		{
			left_align = 1;
			++*format;
		}

		while(isdigit(**format))
		{
			width = width*10 + *(*format)++ - '0';
		}
		c = *(*format)++;

		skip = 0;
		ok = 1;
		buf[0] = '\0';
		switch(c)
		{
			case 'a':
				friendly_size_notation(get_free_space(curr_view->curr_dir), sizeof(buf),
						buf);
				break;
			case 't':
				format_entry_name(curr, NF_FULL, sizeof(buf), buf);
				break;
			case 'T':
				if(curr->type == FT_LINK)
				{
					char full_path[PATH_MAX + 1];
					char link_path[PATH_MAX + 1];  //add by sim1
					get_full_path_of(curr, sizeof(full_path), full_path);
					//mod by sim1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
					if(get_link_target(full_path, link_path, sizeof(link_path)) != 0)
					{
						copy_str(buf, sizeof(buf), "Failed to resolve link");
					}
					else
					{
						snprintf(buf, sizeof(buf), " -> %s", link_path);
					}
					//mod by sim1 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				}
				break;
			case 'f':
				get_short_path_of(view, curr, NF_FULL, 0, sizeof(buf), buf);
				break;
			case 'A':
#ifndef _WIN32
				get_perm_string(buf, sizeof(buf), curr->mode);
#else
				copy_str(buf, sizeof(buf), attr_str_long(curr->attrs));
#endif
				break;
			case 'u':
				get_uid_string(curr, 0, sizeof(buf), buf);
				break;
			case 'g':
				get_gid_string(curr, 0, sizeof(buf), buf);
				break;
			case 's':
				friendly_size_notation(fentry_get_size(view, curr), sizeof(buf), buf);
				break;
			//add by sim1 ************************************************
			case 'r':
				{
					char path[PATH_MAX] = {0};
					get_full_path_at(view, view->list_pos, sizeof(path), path);
					(void)get_rating_string(buf, sizeof(buf), path);
				}
				break;
			case 'n':
				{
					int nitems = !fentry_is_dir(curr) ? 0 : (int)fentry_get_nitems(view, curr);
					snprintf(buf, sizeof(buf), "%d", nitems);
				}
				break;
			//add by sim1 ************************************************
			case 'E':
				{
					uint64_t size = 0U;

					typedef int (*iter_f)(view_t *view, dir_entry_t **entry);
					/* No current element for visual mode, since it can contain truly
					 * empty selection when cursor is on ../ directory. */
					iter_f iter = vle_mode_is(VISUAL_MODE) ? &iter_selected_entries
					                                       : &iter_selection_or_current;

					dir_entry_t *entry = NULL;
					while(iter(view, &entry))
					{
						size += fentry_get_size(view, entry);
					}

					friendly_size_notation(size, sizeof(buf), buf);
				}
				break;
			case 'd':
				{
					struct tm *tm_ptr = localtime(&curr->mtime);
					strftime(buf, sizeof(buf), cfg.time_format, tm_ptr);
				}
				break;
			case '-':
			case 'x':
				skip = expand_num(buf, sizeof(buf), view->filtered);
				break;
			case 'l':
				skip = expand_num(buf, sizeof(buf), view->list_pos + 1);
				break;
			case 'L':
				skip = expand_num(buf, sizeof(buf), view->list_rows + view->filtered);
				break;
			case 'S':
				skip = expand_num(buf, sizeof(buf), view->list_rows);
				break;
			case '%':
				copy_str(buf, sizeof(buf), "%");
				break;
			case 'z':
				copy_str(buf, sizeof(buf), get_tip());
				break;
			case 'D':
				if(curr_stats.number_of_windows == 1)
				{
					view_t *const other = (view == curr_view) ? other_view : curr_view;
					//mod by sim1
					//copy_str(buf, sizeof(buf), replace_home_part(other->curr_dir));
					snprintf(buf, sizeof(buf), " ‖ %s", replace_home_part(other->curr_dir));
				}
				break;
			case '[':
				{
					LineWithAttrs opt = parse_view_macros(view, format, macros, 1);
					copy_str(buf, sizeof(buf), opt.line);
					free(opt.line);

					char *attrs = opt.attrs;
					if(sync_attrs(&result, 0) && opt.attrs_len > 0U)
					{
						if(*attrs != ' ')
						{
							result.attrs[result.attrs_len - 1U] = *attrs;
						}
						++attrs;
					}
					strappend(&result.attrs, &result.attrs_len, attrs);
					free(opt.attrs);
					break;
				}
			case ']':
				if(opt)
				{
					if(nexpansions == 0)
					{
						replace_string(&result.line, "");
						replace_string(&result.attrs, "");
						result.line_len = 0U;
						result.attrs_len = 0U;
					}
					if(sync_attrs(&result, 0))
					{
						result.attrs[--result.attrs_len] = '\0';
					}
					return result;
				}

				LOG_INFO_MSG("Unmatched %%]");
				ok = 0;
				break;
			case '{':
				{
					/* Try to find matching closing bracket
					 * TODO: implement the way to escape it, so that the expr may contain
					 * closing brackets */
					const char *e = strchr(*format, '}');
					char *expr = NULL, *resstr = NULL;
					var_t res = var_false();
					ParsingErrors parsing_error;

					/* If there's no matching closing bracket, just add the opening one
					 * literally */
					if(e == NULL)
					{
						ok = 0;
						break;
					}

					/* Create a NULL-terminated copy of the given expr.
					 * TODO: we could temporarily use buf for that, to avoid extra
					 * allocation, but explicitly named variable reads better. */
					expr = calloc(e - (*format) + 1 /* NUL-term */, 1);
					memcpy(expr, *format, e - (*format));

					/* Try to parse expr, and convert the res to string if succeed. */
					parsing_error = parse(expr, 0, &res);
					if(parsing_error == PE_NO_ERROR)
					{
						resstr = var_to_str(res);
					}

					if(resstr != NULL)
					{
						copy_str(buf, sizeof(buf), resstr);
					}
					else
					{
						copy_str(buf, sizeof(buf), "<Invalid expr>");
					}

					var_free(res);
					free(resstr);
					free(expr);

					*format = e + 1 /* closing bracket */;
				}
				break;
			case '*':
				if(width > 9)
				{
					snprintf(buf, sizeof(buf), "%%%d*", (int)width);
					width = 0;
					break;
				}
				(void)sync_attrs(&result, 1);
				result.attrs[result.attrs_len - 1] = '0' + width;
				width = 0;
				break;

			default:
				LOG_INFO_MSG("Unexpected %%-sequence: %%%c", c);
				ok = 0;
				break;
		}

		if(char_is_one_of("tTAugsEd", c) && fentry_is_fake(curr))
		{
			buf[0] = '\0';
		}

		if(!ok)
		{
			*format = next;
			if(strappendch(&result.line, &result.line_len, '%') != 0)
			{
				break;
			}
			continue;
		}

		check_expanded_str(buf, skip, &nexpansions);
		stralign(buf, width, ' ', left_align);

		if(strappend(&result.line, &result.line_len, buf) != 0)
		{
			break;
		}
	}

	/* Unmatched %[. */
	if(opt)
	{
		(void)strprepend(&result.line, &result.line_len, "%[");
	}

	if(sync_attrs(&result, 0))
	{
		result.attrs[--result.attrs_len] = '\0';
	}
	return result;
}

/* Makes sure that result->attrs has at least as many elements as result->line
 * contains characters + extra_width.  Returns non-zero if result->attrs has
 * extra characters compared to result->line. */
static int
sync_attrs(LineWithAttrs *result, int extra_width)
{
	const size_t nchars = utf8_strsw(result->line) + extra_width;
	if(result->attrs_len < nchars)
	{
		char *const new_attrs = format_str("%s%*s", result->attrs,
				(int)(nchars - result->attrs_len), "");
		free(result->attrs);
		result->attrs = new_attrs;
		result->attrs_len = nchars;
	}
	return (result->attrs_len > nchars);
}

/* Prints number into the buffer.  Returns non-zero if numeric value is
 * "empty" (zero). */
static int
expand_num(char buf[], size_t buf_len, int val)
{
	snprintf(buf, buf_len, "%d", val);
	return (val == 0);
}