static ALLEGRO_PATH *_unix_find_home(void) { char *home_env = getenv("HOME"); if (!home_env || home_env[0] == '\0') { /* since HOME isn't set, we have to ask libc for the info */ /* get user id */ uid_t uid = getuid(); /* grab user information */ struct passwd *pass = getpwuid(uid); if (!pass) { al_set_errno(errno); return NULL; } if (pass->pw_dir) { /* hey, we got our home directory */ return al_create_path_for_directory(pass->pw_dir); } al_set_errno(ENOENT); return NULL; } else { return al_create_path_for_directory(home_env); } }
static ALLEGRO_PATH *follow_symlinks(ALLEGRO_PATH *path) { for (;;) { const char *path_str = al_path_cstr(path, '/'); char buf[PATH_MAX]; int len; len = readlink(path_str, buf, sizeof(buf) - 1); if (len <= 0) break; buf[len] = '\0'; al_destroy_path(path); path = al_create_path(buf); } /* Make absolute path. */ { const char *cwd = al_get_current_directory(); ALLEGRO_PATH *cwd_path = al_create_path_for_directory(cwd); if (al_rebase_path(cwd_path, path)) al_make_path_canonical(path); al_destroy_path(cwd_path); al_free((void *) cwd); } return path; }
static int allua_Path_create_for_directory(lua_State * L) { const char *str = luaL_checkstring(L, 1); ALLUA_path path = al_create_path_for_directory(str); allua_pushPath(L, path, true); return 1; }
/* _find_executable_file: * Helper function: searches path and current directory for executable. * Returns 1 on succes, 0 on failure. */ static ALLEGRO_PATH *_find_executable_file(const char *filename) { char *env; /* If filename has an explicit path, search current directory */ if (strchr(filename, '/')) { if (filename[0] == '/') { /* Full path; done */ return al_create_path(filename); } else { struct stat finfo; char pathname[1024]; /* Prepend current directory */ ALLEGRO_PATH *path = al_get_current_directory(); al_append_path_component(path, filename); if ((stat(pathname, &finfo)==0) && (!S_ISDIR (finfo.st_mode))) { return path; } } } /* If filename has no explicit path, but we do have $PATH, search * there */ else if ((env = getenv("PATH"))) { struct stat finfo; ALLEGRO_USTR *us = al_ustr_new(env); int start_pos = 0; while (start_pos >= 0) { int next_start_pos = al_ustr_find_chr(us, start_pos + 1, ':'); int end_pos = next_start_pos; if (next_start_pos < 0) end_pos = al_ustr_size(us); ALLEGRO_USTR_INFO info; ALLEGRO_USTR *sub = al_ref_ustr(&info, us, start_pos, end_pos); ALLEGRO_PATH *path = al_create_path_for_directory(al_cstr(sub)); al_append_path_component(path, filename); if (stat(al_path_cstr(path, '/'), &finfo) == 0 && !S_ISDIR (finfo.st_mode)) { return path; } start_pos = next_start_pos; } } return NULL; }
/* al_get_standard_path() does not work before the system driver is * initialised. Before that, we need to call the underlying functions * directly. */ static ALLEGRO_PATH *early_get_exename_path(void) { #if defined(ALLEGRO_WINDOWS) return _al_win_get_path(ALLEGRO_EXENAME_PATH); #elif defined(ALLEGRO_MACOSX) return _al_osx_get_path(ALLEGRO_EXENAME_PATH); #elif defined(ALLEGRO_IPHONE) return _al_iphone_get_path(ALLEGRO_EXENAME_PATH); #elif defined(ALLEGRO_UNIX) return _al_unix_get_path(ALLEGRO_EXENAME_PATH); #elif defined(ALLEGRO_ANDROID) return _al_android_get_path(ALLEGRO_EXENAME_PATH); #elif defined(ALLEGRO_SDL) return al_create_path_for_directory(SDL_GetBasePath()); #else #error early_get_exename_path not implemented #endif }
/* _al_win_get_path: * Returns full path to various system and user diretories */ ALLEGRO_PATH *_al_win_get_path(int id) { char path[MAX_PATH]; wchar_t pathw[MAX_PATH]; ALLEGRO_USTR *pathu; uint32_t csidl = 0; HRESULT ret = 0; ALLEGRO_PATH *cisdl_path = NULL; switch (id) { case ALLEGRO_TEMP_PATH: { /* Check: TMP, TMPDIR, TEMP or TEMPDIR */ DWORD ret = GetTempPathW(MAX_PATH, pathw); if (ret > MAX_PATH) { /* should this ever happen, windows is more broken than I ever thought */ return NULL; } pathu = al_ustr_new_from_utf16(pathw); al_ustr_to_buffer(pathu, path, sizeof path); al_ustr_free(pathu); return al_create_path_for_directory(path); } break; case ALLEGRO_RESOURCES_PATH: { /* where the program is in */ HANDLE process = GetCurrentProcess(); char *ptr; GetModuleFileNameExW(process, NULL, pathw, MAX_PATH); pathu = al_ustr_new_from_utf16(pathw); al_ustr_to_buffer(pathu, path, sizeof path); al_ustr_free(pathu); ptr = strrchr(path, '\\'); if (!ptr) { /* shouldn't happen */ return NULL; } /* chop off everything including and after the last slash */ /* should this not chop the slash? */ *ptr = '\0'; return al_create_path_for_directory(path); } break; case ALLEGRO_USER_DATA_PATH: /* CSIDL_APPDATA */ case ALLEGRO_USER_SETTINGS_PATH: csidl = CSIDL_APPDATA; break; case ALLEGRO_USER_HOME_PATH: /* CSIDL_PROFILE */ csidl = CSIDL_PROFILE; break; case ALLEGRO_USER_DOCUMENTS_PATH: /* CSIDL_PERSONAL */ csidl = CSIDL_PERSONAL; break; case ALLEGRO_EXENAME_PATH: { /* full path to the exe including its name */ HANDLE process = GetCurrentProcess(); GetModuleFileNameExW(process, NULL, pathw, MAX_PATH); pathu = al_ustr_new_from_utf16(pathw); al_ustr_to_buffer(pathu, path, sizeof path); al_ustr_free(pathu); return al_create_path(path); } break; default: return NULL; } ret = SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, pathw); if (ret != S_OK) { return NULL; } pathu = al_ustr_new_from_utf16(pathw); al_ustr_to_buffer(pathu, path, sizeof path); al_ustr_free(pathu); cisdl_path = al_create_path_for_directory(path); if (!cisdl_path) return NULL; if (csidl == CSIDL_APPDATA) { const char *org_name = al_get_org_name(); const char *app_name = al_get_app_name(); if (!app_name || !app_name[0]) { /* this shouldn't ever happen. */ al_destroy_path(cisdl_path); return NULL; } if (org_name && org_name[0]) { al_append_path_component(cisdl_path, org_name); } al_append_path_component(cisdl_path, app_name); } return cisdl_path; }
bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display, ALLEGRO_NATIVE_DIALOG *fd) { OPENFILENAME ofn; ALLEGRO_DISPLAY_WIN *win_display; int flags = 0; bool ret; char buf[4096] = ""; ALLEGRO_USTR *filter_string = NULL; win_display = (ALLEGRO_DISPLAY_WIN *)display; if (fd->flags & ALLEGRO_FILECHOOSER_FOLDER) { return select_folder(win_display, fd); } /* Selecting a file. */ memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = (win_display) ? win_display->window : NULL; /* Create filter string. */ if (fd->fc_patterns) { filter_string = create_filter_string(fd->fc_patterns); ofn.lpstrFilter = al_cstr(filter_string); } else { /* List all files by default. */ ofn.lpstrFilter = "All Files\0*.*\0\0"; } ofn.lpstrFile = buf; ofn.nMaxFile = sizeof(buf); if (fd->fc_initial_path) { ofn.lpstrInitialDir = al_path_cstr(fd->fc_initial_path, ALLEGRO_NATIVE_PATH_SEP); } if (fd->title) ofn.lpstrTitle = al_cstr(fd->title); flags |= OFN_NOCHANGEDIR | OFN_EXPLORER; if (fd->flags & ALLEGRO_FILECHOOSER_SAVE) { flags |= OFN_OVERWRITEPROMPT; } else { flags |= (fd->flags & ALLEGRO_FILECHOOSER_FILE_MUST_EXIST) ? OFN_FILEMUSTEXIST : 0; } flags |= (fd->flags & ALLEGRO_FILECHOOSER_MULTIPLE) ? OFN_ALLOWMULTISELECT : 0; flags |= (fd->flags & ALLEGRO_FILECHOOSER_SHOW_HIDDEN) ? 0x10000000 : 0; // NOTE: 0x10000000 is FORCESHOWHIDDEN ofn.Flags = flags; if (flags & OFN_OVERWRITEPROMPT) { ret = GetSaveFileName(&ofn); } else { ret = GetOpenFileName(&ofn); } al_ustr_free(filter_string); if (!ret) { DWORD err = GetLastError(); if (err != ERROR_SUCCESS) { char buf[1000]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, buf, sizeof(buf), NULL); ALLEGRO_ERROR("al_show_native_file_dialog failed: %s\n", buf); } return false; } if (flags & OFN_ALLOWMULTISELECT) { int i; /* Count number of file names in buf. */ fd->fc_path_count = 0; i = skip_nul_terminated_string(buf); while (1) { if (buf[i] == '\0') { fd->fc_path_count++; if (buf[i+1] == '\0') break; } i++; } } else { fd->fc_path_count = 1; } if (fd->fc_path_count == 1) { fd->fc_paths = al_malloc(sizeof(void *)); fd->fc_paths[0] = al_create_path(buf); } else { int i, p; /* If multiple files were selected, the first string in buf is the * directory name, followed by each of the file names terminated by NUL. */ fd->fc_paths = al_malloc(fd->fc_path_count * sizeof(void *)); i = skip_nul_terminated_string(buf); for (p = 0; p < (int)fd->fc_path_count; p++) { fd->fc_paths[p] = al_create_path_for_directory(buf); al_set_path_filename(fd->fc_paths[p], buf+i); i += skip_nul_terminated_string(buf+i); } } return true; }
/* * Parse a map file into an `ALLEGRO_MAP` struct. By default, relative values of `dir` * will be resolved relative to the path of the running executable. To change this, * call `al_find_resources_as(RELATIVE_TO_CWD);` first. */ ALLEGRO_MAP *al_open_map(const char *dir, const char *filename) { char *cwd = al_get_current_directory(); if (!cwd) { fprintf(stderr, "failed to get cwd; errno = %d", al_get_errno()); return NULL; } // `resources` will point to either cwd or the location of the running executable, // depending on what was passed in for `rel`. ALLEGRO_PATH *resources; switch (resources_rel_to) { case RELATIVE_TO_EXE: resources = al_get_standard_path(ALLEGRO_RESOURCES_PATH); break; case RELATIVE_TO_CWD: resources = al_create_path_for_directory(cwd); break; default: fprintf(stderr, "unexpected value for `resources_rel_to` in al_open_map(): %d\n", resources_rel_to); al_free(cwd); return NULL; } ALLEGRO_PATH *map_dir = al_create_path_for_directory(dir); // Change directory to <cwd>/dir if dir is relative, otherwise dir. ALLEGRO_PATH *new_path = (al_join_paths(resources, map_dir) ? resources : map_dir); const char *new_path_cstr = al_path_cstr(new_path, ALLEGRO_NATIVE_PATH_SEP); if (!al_change_directory(new_path_cstr)) { fprintf(stderr, "Error: failed to cd into `%s` in al_open_map().\n", new_path_cstr); al_destroy_path(resources); al_destroy_path(map_dir); al_free(cwd); return NULL; } al_destroy_path(resources); al_destroy_path(map_dir); // Read in the data file xmlDoc *doc = xmlReadFile(filename, NULL, 0); if (!doc) { fprintf(stderr, "Error: failed to parse map data: %s\n", filename); al_free(cwd); return NULL; } // Get the root element, <map> xmlNode *root = xmlDocGetRootElement(doc); // Get some basic info ALLEGRO_MAP *map = MALLOC(ALLEGRO_MAP); map->width = atoi(get_xml_attribute(root, "width")); map->height = atoi(get_xml_attribute(root, "height")); map->tile_width = atoi(get_xml_attribute(root, "tilewidth")); map->tile_height = atoi(get_xml_attribute(root, "tileheight")); map->orientation = g_strdup(get_xml_attribute(root, "orientation")); map->tile_layer_count = 0; map->object_layer_count = 0; // Get the tilesets GSList *tilesets = get_children_for_name(root, "tileset"); map->tilesets = NULL; GSList *tileset_item = tilesets; while (tileset_item) { xmlNode *tileset_node = (xmlNode*)tileset_item->data; tileset_item = g_slist_next(tileset_item); ALLEGRO_MAP_TILESET *tileset = MALLOC(ALLEGRO_MAP_TILESET); tileset->firstgid = atoi(get_xml_attribute(tileset_node, "firstgid")); tileset->tilewidth = atoi(get_xml_attribute(tileset_node, "tilewidth")); tileset->tileheight = atoi(get_xml_attribute(tileset_node, "tileheight")); tileset->name = g_strdup(get_xml_attribute(tileset_node, "name")); // Get this tileset's image xmlNode *image_node = get_first_child_for_name(tileset_node, "image"); tileset->width = atoi(get_xml_attribute(image_node, "width")); tileset->height = atoi(get_xml_attribute(image_node, "height")); tileset->source = g_strdup(get_xml_attribute(image_node, "source")); tileset->bitmap = al_load_bitmap(tileset->source); if (!tileset->bitmap) { fprintf(stderr, "Error: no bitmap available; did you load the image addon?\n"); } // Get this tileset's tiles GSList *tiles = get_children_for_name(tileset_node, "tile"); tileset->tiles = NULL; GSList *tile_item = tiles; while (tile_item) { xmlNode *tile_node = (xmlNode*)tile_item->data; tile_item = g_slist_next(tile_item); ALLEGRO_MAP_TILE *tile = MALLOC(ALLEGRO_MAP_TILE); tile->id = tileset->firstgid + atoi(get_xml_attribute(tile_node, "id")); tile->tileset = tileset; tile->bitmap = NULL; // Get this tile's properties tile->properties = parse_properties(tile_node); // TODO: add a destructor tileset->tiles = g_slist_prepend(tileset->tiles, tile); } g_slist_free(tiles); //tileset->tiles = g_slist_reverse(tileset->tiles); // TODO: add a destructor map->tilesets = g_slist_prepend(map->tilesets, tileset); } g_slist_free(tilesets); //map->tilesets = g_slist_reverse(map->tilesets); // Create the map's master list of tiles cache_tile_list(map); // Get the layers GSList *layers = get_children_for_either_name(root, "layer", "objectgroup"); map->layers = NULL; GSList *layer_item = layers; while (layer_item) { xmlNode *layer_node = (xmlNode*)layer_item->data; layer_item = g_slist_next(layer_item); ALLEGRO_MAP_LAYER *layer = MALLOC(ALLEGRO_MAP_LAYER); layer->name = g_strdup(get_xml_attribute(layer_node, "name")); layer->properties = parse_properties(layer_node); char *layer_visible = get_xml_attribute(layer_node, "visible"); layer->visible = (layer_visible != NULL ? atoi(layer_visible) : 1); char *layer_opacity = get_xml_attribute(layer_node, "opacity"); layer->opacity = (layer_opacity != NULL ? atof(layer_opacity) : 1.0); if (!strcmp((const char*)layer_node->name, "layer")) { layer->type = TILE_LAYER; layer->width = atoi(get_xml_attribute(layer_node, "width")); layer->height = atoi(get_xml_attribute(layer_node, "height")); decode_layer_data(get_first_child_for_name(layer_node, "data"), layer); // Create any missing tile objects unsigned i, j; for (i = 0; i < layer->height; i++) { for (j = 0; j < layer->width; j++) { char id = al_get_single_tile_id(layer, j, i); if (id == 0) { continue; } ALLEGRO_MAP_TILE *tile = al_get_tile_for_id(map, id); if (!tile) { // wasn't defined in the map file, presumably because it had no properties tile = MALLOC(ALLEGRO_MAP_TILE); tile->id = id; tile->properties = g_hash_table_new(NULL, NULL); tile->tileset = NULL; tile->bitmap = NULL; // locate its tilemap GSList *tilesets = map->tilesets; ALLEGRO_MAP_TILESET *tileset_ref; while (tilesets) { ALLEGRO_MAP_TILESET *tileset = (ALLEGRO_MAP_TILESET*)tilesets->data; tilesets = g_slist_next(tilesets); if (tileset->firstgid <= id) { if (!tile->tileset || tileset->firstgid > tile->tileset->firstgid) { tileset_ref = tileset; } } } tile->tileset = tileset_ref; tileset_ref->tiles = g_slist_prepend(tileset_ref->tiles, tile); g_hash_table_insert(map->tiles, GINT_TO_POINTER(tile->id), tile); } // create this tile's bitmap if it hasn't been yet if (!tile->bitmap) { ALLEGRO_MAP_TILESET *tileset = tile->tileset; int id = tile->id - tileset->firstgid; int width = tileset->width / tileset->tilewidth; int x = (id % width) * tileset->tilewidth; int y = (id / width) * tileset->tileheight; if (tileset->bitmap) { tile->bitmap = al_create_sub_bitmap( tileset->bitmap, x, y, tileset->tilewidth, tileset->tileheight); } } } } map->tile_layer_count++; map->tile_layers = g_slist_prepend(map->tile_layers, layer); } else if (!strcmp((const char*)layer_node->name, "objectgroup")) { layer->type = OBJECT_LAYER; layer->objects = NULL; layer->object_count = 0; // TODO: color? GSList *objects = get_children_for_name(layer_node, "object"); GSList *object_item = objects; while (object_item) { xmlNode *object_node = (xmlNode*)object_item->data; object_item = g_slist_next(object_item); ALLEGRO_MAP_OBJECT *object = MALLOC(ALLEGRO_MAP_OBJECT); object->layer = layer; object->name = g_strdup(get_xml_attribute(object_node, "name")); object->type = g_strdup(get_xml_attribute(object_node, "type")); object->x = atoi(get_xml_attribute(object_node, "x")); object->y = atoi(get_xml_attribute(object_node, "y")); object->bitmap = NULL; char *object_width = get_xml_attribute(object_node, "width"); object->width = (object_width ? atoi(object_width) : 0); char *object_height = get_xml_attribute(object_node, "height"); object->height = (object_height ? atoi(object_height) : 0); char *gid = get_xml_attribute(object_node, "gid"); object->gid = (gid ? atoi(gid) : 0); char *object_visible = get_xml_attribute(object_node, "visible"); object->visible = (object_visible ? atoi(object_visible) : 1); // Get the object's properties object->properties = parse_properties(object_node); layer->objects = g_slist_prepend(layer->objects, object); layer->object_count++; } map->object_layer_count++; map->object_layers = g_slist_prepend(map->object_layers, layer); } else { fprintf(stderr, "Error: found invalid layer node \"%s\"\n", layer_node->name); continue; } map->layers = g_slist_prepend(map->layers, layer); } g_slist_free(layers); // If any objects have a tile gid, cache their image layer_item = map->layers; while (layer_item) { ALLEGRO_MAP_LAYER *layer = (ALLEGRO_MAP_LAYER*)layer_item->data; layer_item = g_slist_next(layer_item); if (layer->type != OBJECT_LAYER) { continue; } GSList *objects = layer->objects; while (objects) { ALLEGRO_MAP_OBJECT *object = (ALLEGRO_MAP_OBJECT*)objects->data; objects = g_slist_next(objects); if (!object->gid) { continue; } object->bitmap = al_get_tile_for_id(map, object->gid)->bitmap; object->width = map->tile_width; object->height = map->tile_height; } } xmlFreeDoc(doc); al_change_directory(cwd); al_free(cwd); return map; }
ALLEGRO_PATH *_al_unix_get_path(int id) { switch (id) { case ALLEGRO_TEMP_PATH: { /* Check: TMP, TMPDIR, TEMP or TEMPDIR */ char *envs[] = { "TMP", "TMPDIR", "TEMP", "TEMPDIR", NULL}; uint32_t i = 0; for (; envs[i] != NULL; ++i) { char *tmp = getenv(envs[i]); if (tmp) { return al_create_path_for_directory(tmp); } } /* next try: /tmp /var/tmp /usr/tmp */ char *paths[] = { "/tmp/", "/var/tmp/", "/usr/tmp/", NULL }; for (i=0; paths[i] != NULL; ++i) { ALLEGRO_FS_ENTRY *fse = al_create_fs_entry(paths[i]); bool found = (al_get_fs_entry_mode(fse) & ALLEGRO_FILEMODE_ISDIR) != 0; al_destroy_fs_entry(fse); if (found) { return al_create_path_for_directory(paths[i]); } } /* Give up? */ return NULL; } break; case ALLEGRO_RESOURCES_PATH: { ALLEGRO_PATH *exe = get_executable_name(); exe = follow_symlinks(exe); al_set_path_filename(exe, NULL); return exe; } break; case ALLEGRO_USER_DATA_PATH: case ALLEGRO_USER_SETTINGS_PATH: { ALLEGRO_PATH *local_path = NULL; const char *org_name = al_get_org_name(); const char *app_name = al_get_app_name(); /* to avoid writing directly into the user's directory, require at least an app name */ if (!app_name) return NULL; /* find the appropriate path from the xdg environment variables, if possible */ if (id == ALLEGRO_USER_DATA_PATH) { const char *xdg_data_home = getenv("XDG_DATA_HOME"); local_path = al_create_path_for_directory(xdg_data_home ? xdg_data_home : ".local/share"); } else { const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); local_path = al_create_path_for_directory(xdg_config_home ? xdg_config_home : ".config"); } if (!local_path) return NULL; /* if the path is relative, prepend the user's home directory */ if (al_path_cstr(local_path, '/')[0] != '/') { ALLEGRO_PATH *home_path = _unix_find_home(); if (!home_path) return NULL; al_rebase_path(home_path, local_path); al_destroy_path(home_path); } /* only add org name if not blank */ if (org_name && org_name[0]) { al_append_path_component(local_path, al_get_org_name()); } al_append_path_component(local_path, al_get_app_name()); return local_path; } break; case ALLEGRO_USER_HOME_PATH: return _unix_find_home(); case ALLEGRO_USER_DOCUMENTS_PATH: { ALLEGRO_PATH *local_path = _get_xdg_path("DOCUMENTS"); return local_path ? local_path : _unix_find_home(); } break; case ALLEGRO_EXENAME_PATH: return get_executable_name(); break; default: return NULL; } return NULL; }
/* get_xdg_path - locate an XDG user dir */ static ALLEGRO_PATH *_get_xdg_path(const char *location) { ALLEGRO_PATH *location_path = NULL; ALLEGRO_PATH *xdg_config_path = NULL; ALLEGRO_FILE *xdg_config_file = NULL; const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); int fd; if (xdg_config_home) { /* use $XDG_CONFIG_HOME since it exists */ xdg_config_path = al_create_path_for_directory(xdg_config_home); } else { /* the default XDG location is ~/.config */ xdg_config_path = al_get_standard_path(ALLEGRO_USER_HOME_PATH); if (!xdg_config_path) return NULL; al_append_path_component(xdg_config_path, ".config"); } al_set_path_filename(xdg_config_path, "user-dirs.dirs"); fd = open(al_path_cstr(xdg_config_path, '/'), O_RDONLY); if (fd != -1) { xdg_config_file = al_fopen_fd(fd, "r"); } al_destroy_path(xdg_config_path); if (!xdg_config_file) return NULL; while (!al_feof(xdg_config_file)) { char line[XDG_MAX_PATH_LEN]; /* one line of the config file */ const char *p = line; /* where we're at in the line */ char component[XDG_MAX_PATH_LEN]; /* the path component being parsed */ int i = 0; /* how long the current component is */ al_fgets(xdg_config_file, line, XDG_MAX_PATH_LEN); /* skip leading white space */ while (*p == ' ' || *p == '\t') p++; /* skip the line if it does not begin with XDG_location_DIR */ if (strncmp(p, "XDG_", 4)) continue; p += 4; if (strncmp(p, location, strlen(location))) continue; p += strlen(location); if (strncmp(p, "_DIR", 4)) continue; p += 4; /* skip past the =", allowing for white space */ while (*p == ' ' || *p == '\t') p++; if (*p++ != '=') continue; while (*p == ' ' || *p == '\t') p++; if (*p++ != '"') continue; /* We've found the right line. Now parse it, basically assuming that it is in a sane format. */ if (!strncmp(p, "$HOME", 5)) { /* $HOME is the only environment variable that the path is allowed to use, and it must be first, by specification. */ location_path = al_get_standard_path(ALLEGRO_USER_HOME_PATH); p += 5; } else { location_path = al_create_path("/"); } while (*p) { if (*p == '"' || *p == '/') { /* add the component (if non-empty) to the path */ if (i > 0) { component[i] = 0; al_append_path_component(location_path, component); i = 0; } if (*p == '"') break; } else { if (*p == '\\') { /* treat any escaped character as a literal */ p++; if (!*p) break; } component[i++] = *p; } p++; } /* Finished parsing the path. */ break; } al_fclose(xdg_config_file); return location_path; }