refactor: Add original .c package files by Jef.
							parent
							
								
									f376aedaee
								
							
						
					
					
						commit
						c2313fe0be
					
				| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					#ifndef VIETER_DYNARRAY
 | 
				
			||||||
 | 
					#define VIETER_DYNARRAY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct dyn_array DynArray;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DynArray *dynarray_init(size_t initial_capacity);
 | 
				
			||||||
 | 
					void dynarray_add(DynArray *da, const char * s);
 | 
				
			||||||
 | 
					void dynarray_free(DynArray *da);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Convert a DynArray into an array by freeing all its surrounding components
 | 
				
			||||||
 | 
					 * and returning the underlying array pointer.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					char **dynarray_convert(DynArray *da);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					#ifndef VIETER_PACKAGE
 | 
				
			||||||
 | 
					#define VIETER_PACKAGE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "archive.h"
 | 
				
			||||||
 | 
					#include "archive_entry.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "package_info.h"
 | 
				
			||||||
 | 
					#include "dynarray.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct pkg {
 | 
				
			||||||
 | 
						char *path;
 | 
				
			||||||
 | 
						PkgInfo *info;
 | 
				
			||||||
 | 
						DynArray *files;
 | 
				
			||||||
 | 
						int compression;
 | 
				
			||||||
 | 
					} Pkg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pkg *package_read_archive(const char *pkg_path);
 | 
				
			||||||
 | 
					void package_free(Pkg ** ptp);
 | 
				
			||||||
 | 
					char *package_to_description(Pkg *pkg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					#ifndef VIETER_PACKAGE_INFO
 | 
				
			||||||
 | 
					#define VIETER_PACKAGE_INFO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FREE_STRING(sp) if (sp != NULL) free(sp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "dynarray.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct pkg_info {
 | 
				
			||||||
 | 
						char *name;
 | 
				
			||||||
 | 
						char *base;
 | 
				
			||||||
 | 
						char *version;
 | 
				
			||||||
 | 
						char *description;
 | 
				
			||||||
 | 
						int64_t size;
 | 
				
			||||||
 | 
						int64_t csize;
 | 
				
			||||||
 | 
						char *url;
 | 
				
			||||||
 | 
						char *arch;
 | 
				
			||||||
 | 
						int64_t build_date;
 | 
				
			||||||
 | 
						char *packager;
 | 
				
			||||||
 | 
						char *pgpsig;
 | 
				
			||||||
 | 
						int64_t pgpsigsize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DynArray *groups;
 | 
				
			||||||
 | 
						DynArray *licenses;
 | 
				
			||||||
 | 
						DynArray *replaces;
 | 
				
			||||||
 | 
						DynArray *depends;
 | 
				
			||||||
 | 
						DynArray *conflicts;
 | 
				
			||||||
 | 
						DynArray *provides;
 | 
				
			||||||
 | 
						DynArray *optdepends;
 | 
				
			||||||
 | 
						DynArray *makedepends;
 | 
				
			||||||
 | 
						DynArray *checkdepends;
 | 
				
			||||||
 | 
					} PkgInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PkgInfo *package_info_init();
 | 
				
			||||||
 | 
					int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str);
 | 
				
			||||||
 | 
					void package_info_free(PkgInfo *pkg_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					#include "dynarray.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct dyn_array {
 | 
				
			||||||
 | 
						char **array;
 | 
				
			||||||
 | 
						size_t size;
 | 
				
			||||||
 | 
						size_t capacity;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DynArray *dynarray_init(size_t initial_capacity) {
 | 
				
			||||||
 | 
						DynArray *da = malloc(sizeof(DynArray));
 | 
				
			||||||
 | 
						da->size = 0;
 | 
				
			||||||
 | 
						da->capacity = initial_capacity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return da;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dynarray_add(DynArray *da, const char *s) {
 | 
				
			||||||
 | 
						// An empty dynarray does not have an allocated internal array yet
 | 
				
			||||||
 | 
						if (da->size == 0) {
 | 
				
			||||||
 | 
							da->array = malloc(sizeof(char*) * da->capacity);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Double array size if it's full
 | 
				
			||||||
 | 
						else if (da->size == da->capacity) {
 | 
				
			||||||
 | 
							da->array = realloc(da->array, da->capacity * 2);
 | 
				
			||||||
 | 
							da->capacity *= 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						da->array[da->size] = strdup(s);
 | 
				
			||||||
 | 
						da->size++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dynarray_free(DynArray *da) {
 | 
				
			||||||
 | 
						if (da == NULL) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (da->array != NULL) {
 | 
				
			||||||
 | 
							for (size_t i = 0; i < da->size; i++) {
 | 
				
			||||||
 | 
								free(da->array[i]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							free(da->array);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free(da);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char **dynarray_convert(DynArray *da) {
 | 
				
			||||||
 | 
						char **array = da->array;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						da->array = NULL;
 | 
				
			||||||
 | 
						dynarray_free(da);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return array;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,98 @@
 | 
				
			||||||
 | 
					#include "package.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static char *ignored_names[5] = {
 | 
				
			||||||
 | 
						".BUILDINFO",
 | 
				
			||||||
 | 
						".INSTALL",
 | 
				
			||||||
 | 
						".MTREE",
 | 
				
			||||||
 | 
						".PKGINFO",
 | 
				
			||||||
 | 
						".CHANGELOG"
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static int ignored_words_len = sizeof(ignored_names) / sizeof(char *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pkg *package_init() {
 | 
				
			||||||
 | 
						return calloc(sizeof(PkgInfo), 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pkg *package_read_archive(const char *pkg_path) {
 | 
				
			||||||
 | 
						struct archive *a = archive_read_new();
 | 
				
			||||||
 | 
						struct archive_entry *entry = archive_entry_new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// These three are the most commonly used compression methods
 | 
				
			||||||
 | 
						archive_read_support_filter_zstd(a);
 | 
				
			||||||
 | 
						archive_read_support_filter_gzip(a);
 | 
				
			||||||
 | 
						archive_read_support_filter_xz(a);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Contents should always be a tarball
 | 
				
			||||||
 | 
						archive_read_support_format_tar(a);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO where does this 10240 come from?
 | 
				
			||||||
 | 
						int r = archive_read_open_filename(a, pkg_path, 10240);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Exit early if we weren't able to successfully open the archive for reading
 | 
				
			||||||
 | 
						if (r != ARCHIVE_OK) {
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int compression_code = archive_filter_code(a, 0);
 | 
				
			||||||
 | 
						const char *path_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						PkgInfo *pkg_info;
 | 
				
			||||||
 | 
						DynArray *files = dynarray_init(16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
 | 
				
			||||||
 | 
							path_name = archive_entry_pathname(entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool ignore = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (size_t i = 0; i < ignored_words_len; i++) {
 | 
				
			||||||
 | 
								if (strcmp(path_name, ignored_names[i]) == 0) {
 | 
				
			||||||
 | 
									ignore = true;
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!ignore) {
 | 
				
			||||||
 | 
								dynarray_add(files, path_name);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (strcmp(path_name, ".PKGINFO") == 0) {
 | 
				
			||||||
 | 
								// Read data of file into memory buffer
 | 
				
			||||||
 | 
								int size = archive_entry_size(entry);
 | 
				
			||||||
 | 
								char *buf = malloc(size);
 | 
				
			||||||
 | 
								archive_read_data(a, buf, size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Parse package info string into a struct
 | 
				
			||||||
 | 
								pkg_info = package_info_init();
 | 
				
			||||||
 | 
								package_info_parse(pkg_info, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								free(buf);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								archive_read_data_skip(a);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get size of file
 | 
				
			||||||
 | 
						struct stat stats;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (stat(pkg_path, &stats) != 0) {
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkg_info->csize = stats.st_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						archive_read_free(a);
 | 
				
			||||||
 | 
						archive_entry_free(entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create final return value
 | 
				
			||||||
 | 
						Pkg *pkg = package_init();
 | 
				
			||||||
 | 
						pkg->path = strdup(pkg_path);
 | 
				
			||||||
 | 
						pkg->info = pkg_info;
 | 
				
			||||||
 | 
						pkg->files = files;
 | 
				
			||||||
 | 
						pkg->compression = compression_code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pkg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					char *package_to_description(Pkg *pkg) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,139 @@
 | 
				
			||||||
 | 
					#include <stdbool.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "package_info.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PkgInfo *package_info_init() {
 | 
				
			||||||
 | 
						return calloc(1, sizeof(PkgInfo));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void package_info_free(PkgInfo *pkg_info) {
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->name);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->base);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->version);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->description);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->url);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->arch);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->packager);
 | 
				
			||||||
 | 
						FREE_STRING(pkg_info->pgpsig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->groups);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->licenses);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->replaces);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->depends);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->conflicts);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->provides);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->optdepends);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->makedepends);
 | 
				
			||||||
 | 
						dynarray_free(pkg_info->checkdepends);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free(pkg_info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Advance the pointer until all spaces are skipped.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline char *trim_spaces_front(char *ptr) {
 | 
				
			||||||
 | 
						while (ptr[0] == ' ') {
 | 
				
			||||||
 | 
							ptr++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Given a string pointer in the middle of a string, move over all spaces in the
 | 
				
			||||||
 | 
					 * given direction. The final space is replaced with a NULL-byte.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void trim_spaces_back(char *ptr) {
 | 
				
			||||||
 | 
						if (ptr[0] != ' ') {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (ptr[-1] == ' ') {
 | 
				
			||||||
 | 
							ptr--;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ptr[0] = '\0';
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PKG_INFO_STRING(key, field) if (strcmp(key_ptr, key) == 0) { pkg_info->field = strdup(value_ptr); goto advance; }
 | 
				
			||||||
 | 
					#define PKG_INFO_INT(key, field) if (strcmp(key_ptr, key) == 0) { pkg_info->field = atoi(value_ptr); goto advance; }
 | 
				
			||||||
 | 
					#define PKG_INFO_ARRAY(key, field) if (strcmp(key_ptr, key) == 0) { \
 | 
				
			||||||
 | 
						if (pkg_info->field == NULL) { pkg_info->field = dynarray_init(4); } \
 | 
				
			||||||
 | 
						dynarray_add(pkg_info->field, value_ptr); goto advance; \
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str) {
 | 
				
			||||||
 | 
						char *offset_ptr, *equals_ptr, *key_ptr, *value_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool end = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Iterate over all lines in file
 | 
				
			||||||
 | 
						while (!end) {
 | 
				
			||||||
 | 
							// This pointer will always point to the final character of the
 | 
				
			||||||
 | 
							// current line, be it the position of a newline or the NULL byte at
 | 
				
			||||||
 | 
							// the end of the entire string
 | 
				
			||||||
 | 
							offset_ptr = strchr(pkg_info_str, '\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// We replace the newline with a NULL byte. Now we know the line runs
 | 
				
			||||||
 | 
							// until the next NULL byte.
 | 
				
			||||||
 | 
							if (offset_ptr != NULL) {
 | 
				
			||||||
 | 
								offset_ptr[0] = '\0';
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								// Advance pointer to the NULL byte of the string
 | 
				
			||||||
 | 
								offset_ptr = pkg_info_str + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while (*offset_ptr != '\0') {
 | 
				
			||||||
 | 
									offset_ptr++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								end = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Skip comment lines
 | 
				
			||||||
 | 
							if (pkg_info_str[0] == '#') {
 | 
				
			||||||
 | 
								goto advance;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							equals_ptr = strchr(pkg_info_str, '=');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// If a line doesn't contain an equals sign, the file is invalid
 | 
				
			||||||
 | 
							if (equals_ptr == NULL) {
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Trim whitespace from key
 | 
				
			||||||
 | 
							key_ptr = trim_spaces_front(pkg_info_str);
 | 
				
			||||||
 | 
							trim_spaces_back(equals_ptr - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Trim spaces from value
 | 
				
			||||||
 | 
							value_ptr = trim_spaces_front(equals_ptr + 1);
 | 
				
			||||||
 | 
							trim_spaces_back(offset_ptr - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Match key
 | 
				
			||||||
 | 
							PKG_INFO_STRING("pkgname", name);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("pkgbase", base);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("pkgver", version);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("pkgdesc", description);
 | 
				
			||||||
 | 
							PKG_INFO_INT("size", size);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("url", url);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("arch", arch);
 | 
				
			||||||
 | 
							PKG_INFO_INT("builddate", build_date);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("packager", packager);
 | 
				
			||||||
 | 
							PKG_INFO_STRING("pgpsig", pgpsig);
 | 
				
			||||||
 | 
							PKG_INFO_INT("pgpsigsize", pgpsigsize);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("group", groups);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("license", licenses);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("replaces", replaces);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("depend", depends);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("optdepend", optdepends);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("makedepend", makedepends);
 | 
				
			||||||
 | 
							PKG_INFO_ARRAY("checkdepend", checkdepends);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					advance:
 | 
				
			||||||
 | 
							pkg_info_str = offset_ptr + 1;
 | 
				
			||||||
 | 
							continue;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue