1070 lines
26 KiB
C
1070 lines
26 KiB
C
/*
|
|
* pacman
|
|
*
|
|
* Copyright (c) 2002 by Judd Vinet <jvinet@zeroflux.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*/
|
|
|
|
#include "pacman.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <libtar.h>
|
|
#include <zlib.h>
|
|
|
|
/* borrowed and modifed from Per Liden's pkgutils (http://crux.nu) */
|
|
static int gzopen_frontend(char *pathname, int oflags, int mode)
|
|
{
|
|
char* gzoflags;
|
|
int fd;
|
|
gzFile gzf;
|
|
|
|
switch (oflags & O_ACCMODE) {
|
|
case O_WRONLY:
|
|
gzoflags = "w";
|
|
break;
|
|
case O_RDONLY:
|
|
gzoflags = "r";
|
|
break;
|
|
case O_RDWR:
|
|
default:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if((fd = open(pathname, oflags, mode)) == -1) {
|
|
return -1;
|
|
}
|
|
if((oflags & O_CREAT) && fchmod(fd, mode)) {
|
|
return -1;
|
|
}
|
|
if(!(gzf = gzdopen(fd, gzoflags))) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
return (int)gzf;
|
|
}
|
|
|
|
static tartype_t gztype = {
|
|
(openfunc_t) gzopen_frontend,
|
|
(closefunc_t)gzclose,
|
|
(readfunc_t) gzread,
|
|
(writefunc_t)gzwrite
|
|
};
|
|
|
|
/*
|
|
* GLOBALS
|
|
*
|
|
*/
|
|
FILE* dbfp = NULL;
|
|
char* dbpath = PKGDB;
|
|
char* pkgname = NULL;
|
|
char* pkgver = NULL;
|
|
|
|
char* pmo_root = NULL;
|
|
unsigned short pmo_verbose = 0;
|
|
unsigned short pmo_force = 0;
|
|
unsigned short pmo_upgrade = 0;
|
|
unsigned short pmo_nosave = 0;
|
|
unsigned short pmo_nofunc = 0;
|
|
unsigned short pmo_q_isfile = 0;
|
|
unsigned short pmo_q_info = 0;
|
|
unsigned short pmo_q_list = 0;
|
|
char* pmo_q_owns = NULL;
|
|
|
|
pkginfo_t** packages = NULL;
|
|
unsigned int pkgcount = 0;
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
pm_opfunc_t op_func;
|
|
char* funcvar = NULL;
|
|
int ret = 0;
|
|
|
|
/* default root */
|
|
pmo_root = (char*)malloc(2);
|
|
strcpy(pmo_root, "/");
|
|
|
|
if(argc < 2) {
|
|
usage(PM_MAIN, (char*)basename(argv[0]));
|
|
return(0);
|
|
}
|
|
|
|
/* determine the requested operation and pass off to */
|
|
/* the handler function */
|
|
if(!strcmp(argv[1], "-A") || !strcmp(argv[1], "--add")) {
|
|
op_func = pacman_add;
|
|
funcvar = parseargs(PM_ADD, argc, argv);
|
|
} else if(!strcmp(argv[1], "-R") || !strcmp(argv[1], "--remove")) {
|
|
op_func = pacman_remove;
|
|
funcvar = parseargs(PM_REMOVE, argc, argv);
|
|
} else if(!strcmp(argv[1], "-Q") || !strcmp(argv[1], "--query")) {
|
|
op_func = pacman_query;
|
|
funcvar = parseargs(PM_QUERY, argc, argv);
|
|
} else if(!strcmp(argv[1], "-U") || !strcmp(argv[1], "--upgrade")) {
|
|
op_func = pacman_upgrade;
|
|
funcvar = parseargs(PM_UPGRADE, argc, argv);
|
|
} else if(!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
|
|
usage(PM_MAIN, (char*)basename(argv[0]));
|
|
return(1);
|
|
} else if(!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) {
|
|
version();
|
|
return(1);
|
|
} else {
|
|
printf("error: invalid operation\n\n");
|
|
usage(PM_MAIN, (char*)basename(argv[0]));
|
|
return(1);
|
|
}
|
|
|
|
if(funcvar == NULL && op_func != pacman_query) {
|
|
return(1);
|
|
}
|
|
vprint("Installation Root: %s\n", pmo_root);
|
|
vprint("Package Name: %s\n", funcvar);
|
|
|
|
/* check for db existence */
|
|
if(pmo_root != NULL) {
|
|
/* trim the trailing '/' if there is one */
|
|
if((int)rindex(pmo_root, '/') == ((int)pmo_root)+strlen(pmo_root)-1) {
|
|
pmo_root[strlen(pmo_root)-1] = '\0';
|
|
}
|
|
free(dbpath);
|
|
dbpath = (char*)malloc(strlen(pmo_root) + strlen(PKGDB) + 1);
|
|
strcpy(dbpath, pmo_root);
|
|
dbpath = (char*)strcat(dbpath, PKGDB);
|
|
}
|
|
vprint("Using package DB: %s\n", dbpath);
|
|
|
|
ret = db_open(dbpath);
|
|
if(ret == 1) {
|
|
printf("error: Could not open package database file!\n");
|
|
printf(" Check to see that %s exists.\n", dbpath);
|
|
printf(" If not, you may simply create it by \"touch\"-ing it.\n");
|
|
return(1);
|
|
}
|
|
if(ret == 2) {
|
|
printf("error: Database is corrupt! You may need to use the backup database.\n");
|
|
printf(" I hope you like tweaking... ;-)\n");
|
|
return(1);
|
|
}
|
|
|
|
/* start the requested operation */
|
|
if(!pmo_nofunc) {
|
|
ret = op_func(funcvar);
|
|
if(ret) {
|
|
printf("There were errors\n");
|
|
}
|
|
}
|
|
|
|
fclose(dbfp);
|
|
return(0);
|
|
}
|
|
|
|
int pacman_add(char* pkgfile)
|
|
{
|
|
int i, errors = 0;
|
|
TAR* tar;
|
|
char* errmsg = NULL;
|
|
char* expath = NULL;
|
|
char* newpath = NULL;
|
|
fileset_t files = NULL;
|
|
unsigned int filecount = 0, nb = 0;
|
|
struct stat buf;
|
|
|
|
/* Populate the file list */
|
|
filecount = load_pkg(pkgfile, &files, 0);
|
|
|
|
if(filecount == 0) {
|
|
return(1);
|
|
}
|
|
|
|
/* Now check for conflicts in the db */
|
|
vprint("Looking for DB conflicts...\n");
|
|
if((i = db_find_conflicts(files, filecount)) == 1) {
|
|
if(pmo_force) {
|
|
printf("\nInstalling package anyway...\n");
|
|
printf(" You might have duplicate entries in your package\n");
|
|
printf(" database now. You may want to edit it and remove\n");
|
|
printf(" one of the copies.\n\n");
|
|
} else {
|
|
printf("Aborting...\n");
|
|
printf(" (use -f to override)\n\n");
|
|
return(1);
|
|
}
|
|
} else if(i == 2) {
|
|
return(1);
|
|
} else {
|
|
vprint("No DB conflicts found\n");
|
|
}
|
|
|
|
/* see if this is an upgrade. if so, remove the old package first */
|
|
if(pmo_upgrade) {
|
|
vprint("Removing old package first...\n");
|
|
if(pacman_remove(pkgname)) {
|
|
printf("\nUpgrade aborted.\n");
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
/* open the .tar.gz package */
|
|
if(tar_open(&tar, pkgfile, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
|
|
perror("could not open package");
|
|
return(1);
|
|
}
|
|
vprint("Extracting files...\n");
|
|
for(i = 0; !th_read(tar); i++) {
|
|
if(!strcmp(th_get_pathname(tar), ".PKGINFO")) {
|
|
tar_skip_regfile(tar);
|
|
continue;
|
|
}
|
|
/* build the new pathname relative to pmo_root */
|
|
expath = (char*)malloc(strlen(th_get_pathname(tar))+strlen(pmo_root)+2);
|
|
strcpy(expath, pmo_root);
|
|
strcat(expath, "/");
|
|
strcat(expath, th_get_pathname(tar));
|
|
|
|
/* see if this file should be backed up */
|
|
nb = needbackup(expath, files, filecount);
|
|
|
|
vprint(" %s\n", expath);
|
|
if(!pmo_nosave && nb && !stat(expath, &buf)) {
|
|
/* backup this file */
|
|
newpath = (char*)realloc(newpath, strlen(expath)+6);
|
|
strcpy(newpath, expath);
|
|
strcat(newpath, ".save");
|
|
rename(expath, newpath);
|
|
printf("%s renamed to %s\n", expath, newpath);
|
|
}
|
|
if(tar_extract_file(tar, expath)) {
|
|
errmsg = strerror(errno);
|
|
printf("could not extract %s: %s\n", th_get_pathname(tar), errmsg);
|
|
errors = 1;
|
|
}
|
|
free(expath);
|
|
}
|
|
tar_close(tar);
|
|
vprint("Done.\n");
|
|
if(errors) {
|
|
printf("There were errors. No database update was performed.\n");
|
|
} else {
|
|
vprint("Updating database...\n");
|
|
if(db_update(files, filecount)) {
|
|
printf("error: Could not update database! The database may not\n");
|
|
printf(" be in a sane state!\n");
|
|
return(1);
|
|
}
|
|
vprint("Done.\n");
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int pacman_remove(char* pkgfile)
|
|
{
|
|
int found = 0, done = 0;
|
|
int i;
|
|
char line[255];
|
|
fileset_t files = NULL;
|
|
unsigned int filecount = 0, nb = 0;
|
|
struct stat buf;
|
|
char* newpath = NULL;
|
|
char* file = NULL;
|
|
|
|
if(pkgfile == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
/* find the package's filelist in the db */
|
|
rewind(dbfp);
|
|
while(!found && !feof(dbfp)) {
|
|
fgets(line, 255, dbfp);
|
|
strcpy(line, trim(line));
|
|
if(!strcmp(line, pkgfile)) {
|
|
/* read the version */
|
|
fgets(line, 255, dbfp);
|
|
found = 1;
|
|
}
|
|
}
|
|
if(!found) {
|
|
printf("Cannot remove %s: Package was not found.\n", pkgfile);
|
|
return(1);
|
|
}
|
|
|
|
while(!done) {
|
|
fgets(line, 255, dbfp);
|
|
strcpy(line, trim(line));
|
|
if(strlen(line)) {
|
|
/* add the path to the list */
|
|
files = (fileset_t)realloc(files, (++filecount) * sizeof(char*));
|
|
files[filecount-1] = (char*)malloc(strlen(line)+1);
|
|
strcpy(files[filecount-1], line);
|
|
} else {
|
|
done = 1;
|
|
}
|
|
}
|
|
/* iterate through the list backwards, unlinking files */
|
|
for(i = filecount-1; i >= 0; i--) {
|
|
file = (char*)realloc(file, strlen(files[i])+strlen(pmo_root)+1);
|
|
strcpy(file, pmo_root);
|
|
if(files[i][0] == '*') {
|
|
nb = 1;
|
|
strcat(file, (char*)(files[i]+1));
|
|
} else {
|
|
nb = 0;
|
|
strcat(file, files[i]);
|
|
}
|
|
if(lstat(file, &buf)) {
|
|
vprint("file %s does not exist\n", file);
|
|
continue;
|
|
}
|
|
if(S_ISDIR(buf.st_mode)) {
|
|
vprint(" removing directory %s\n", file);
|
|
if(rmdir(file)) {
|
|
/* this is okay, other packages are probably using it. */
|
|
/* perror("cannot remove directory"); */
|
|
}
|
|
} else {
|
|
/* if the file ends in .conf, back it up */
|
|
if(!pmo_nosave && nb) {
|
|
newpath = (char*)realloc(newpath, strlen(file)+6);
|
|
strcpy(newpath, file);
|
|
strcat(newpath, ".save");
|
|
rename(file, newpath);
|
|
printf("%s renamed to %s\n", file, newpath);
|
|
} else {
|
|
vprint(" unlinking %s\n", file);
|
|
if(unlink(file)) {
|
|
perror("cannot remove file");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now splice this name out of the packages list */
|
|
found = 0;
|
|
for(i = 0; i < pkgcount-1; i++) {
|
|
if(found) {
|
|
packages[i] = packages[i+1];
|
|
} else {
|
|
if(!strcmp(packages[i]->name, pkgfile)) {
|
|
found = 1;
|
|
if(i < pkgcount-1) {
|
|
packages[i] = packages[i+1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* drop the last item */
|
|
packages = (pkginfo_t**)realloc(packages, (--pkgcount)*sizeof(pkginfo_t*));
|
|
|
|
/* and commit the db */
|
|
return(db_update(NULL, 0));
|
|
}
|
|
|
|
int pacman_query(char* pkgfile)
|
|
{
|
|
char *str = NULL;
|
|
char name[255];
|
|
char ver[255];
|
|
char line[255];
|
|
int found = 0;
|
|
int done = 0;
|
|
int i;
|
|
unsigned int filecount = 0;
|
|
fileset_t files = NULL;
|
|
|
|
/* output info for a .tar.gz package */
|
|
if(pmo_q_isfile) {
|
|
filecount = load_pkg(pkgfile, &files, pmo_q_info);
|
|
if(pmo_q_list) {
|
|
for(i = 0; i < filecount; i++) {
|
|
if(strcmp(files[i], ".PKGINFO")) {
|
|
if(files[i][0] == '*') {
|
|
printf("%s\n", (char*)(files[i]+1));
|
|
} else {
|
|
printf("%s\n", files[i]);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
printf("%s %s\n", pkgname, pkgver);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* determine the owner of a file */
|
|
if(pmo_q_owns != NULL) {
|
|
rewind(dbfp);
|
|
while(!found && !feof(dbfp)) {
|
|
fgets(name, 255, dbfp);
|
|
strcpy(name, trim(name));
|
|
fgets(ver, 255, dbfp);
|
|
strcpy(ver, trim(ver));
|
|
strcpy(line, " ");
|
|
while(strlen(line) && !feof(dbfp)) {
|
|
fgets(line, 255, dbfp);
|
|
strcpy(line, trim(line));
|
|
str = line;
|
|
str += strlen(pmo_root);
|
|
if(!strcmp(line, pmo_q_owns)) {
|
|
printf("%s %s\n", name, ver);
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
printf("No package owns this file.\n");
|
|
return(0);
|
|
}
|
|
|
|
/* find packages in the db */
|
|
rewind(dbfp);
|
|
while(!done) {
|
|
found = 0;
|
|
while(!found && !feof(dbfp)) {
|
|
fgets(name, 255, dbfp);
|
|
strcpy(name, trim(name));
|
|
if(pkgfile == NULL || (pkgfile != NULL && !strcmp(name, pkgfile))) {
|
|
/* read the version */
|
|
fgets(ver, 255, dbfp);
|
|
strcpy(ver, trim(ver));
|
|
found = 1;
|
|
if(pkgfile != NULL) {
|
|
done = 1;
|
|
}
|
|
}
|
|
}
|
|
if(feof(dbfp)) {
|
|
if(pkgfile != NULL && !found) {
|
|
printf("Package was not found in database.\n");
|
|
}
|
|
break;
|
|
}
|
|
found = 0;
|
|
while(!found) {
|
|
fgets(line, 255, dbfp);
|
|
strcpy(line, trim(line));
|
|
if(strlen(line)) {
|
|
if(pmo_q_list) {
|
|
if(line[0] == '*') {
|
|
printf("%s%s\n", pmo_root, (char*)(line+1));
|
|
} else {
|
|
printf("%s%s\n", pmo_root, line);
|
|
}
|
|
}
|
|
} else {
|
|
found = 1;
|
|
}
|
|
}
|
|
if(!pmo_q_list) {
|
|
printf("%s %s\n", name, ver);
|
|
}
|
|
if(feof(dbfp)) {
|
|
done = 1;
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
int pacman_upgrade(char* pkgfile)
|
|
{
|
|
/* this is basically just a remove,add process. pacman_add() will */
|
|
/* handle it */
|
|
pmo_upgrade = 1;
|
|
pacman_add(pkgfile);
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Populate the package file list
|
|
* pkgfile: filename of package
|
|
* listptr: this will be set to the new fileset_t
|
|
*
|
|
* Returns: the number of filenames read in the package, or 0 on error
|
|
*
|
|
*/
|
|
int load_pkg(char* pkgfile, fileset_t* listptr, unsigned short output)
|
|
{
|
|
char* expath;
|
|
char* descfile;
|
|
int i;
|
|
TAR* tar;
|
|
unsigned int filecount = 0;
|
|
fileset_t files = NULL;
|
|
fileset_t backup = NULL;
|
|
unsigned int bakct = 0;
|
|
|
|
descfile = (char*)malloc(strlen("/tmp/pacman_XXXXXX")+1);
|
|
strcpy(descfile, "/tmp/pacman_XXXXXX");
|
|
|
|
if(tar_open(&tar, pkgfile, &gztype, O_RDONLY, 0, TAR_GNU) == -1) {
|
|
perror("could not open package");
|
|
return(0);
|
|
}
|
|
vprint("Loading filelist from package...\n");
|
|
for(i = 0; !th_read(tar); i++) {
|
|
if(!strcmp(th_get_pathname(tar), ".PKGINFO")) {
|
|
/* extract this file into /tmp. it has info for us */
|
|
vprint("Found package description file.\n");
|
|
mkstemp(descfile);
|
|
tar_extract_file(tar, descfile);
|
|
parse_descfile(descfile, output, &backup, &bakct);
|
|
continue;
|
|
}
|
|
/* build the new pathname relative to pmo_root */
|
|
if(is_in(th_get_pathname(tar), backup, bakct)) {
|
|
expath = (char*)malloc(strlen(th_get_pathname(tar))+strlen(pmo_root)+3);
|
|
// prepend the backup symbol
|
|
strcpy(expath, "*");
|
|
} else {
|
|
expath = (char*)malloc(strlen(th_get_pathname(tar))+strlen(pmo_root)+2);
|
|
expath[0] = '\0';
|
|
}
|
|
strcat(expath, pmo_root);
|
|
strcat(expath, "/");
|
|
strcat(expath, th_get_pathname(tar));
|
|
/* add the path to the list */
|
|
files = (fileset_t)realloc(files, (++filecount) * sizeof(char*));
|
|
files[filecount-1] = expath;
|
|
|
|
if(TH_ISREG(tar) && tar_skip_regfile(tar)) {
|
|
perror("bad package file");
|
|
return(1);
|
|
}
|
|
expath = NULL;
|
|
}
|
|
tar_close(tar);
|
|
|
|
if(pkgname == NULL || pkgver == NULL) {
|
|
printf("The current version of Pacman requires a .PKGINFO file\n");
|
|
printf("present in the .tar.gz archive. This package does not\n");
|
|
printf("have one.\n");
|
|
return(0);
|
|
}
|
|
|
|
(*listptr) = files;
|
|
return(filecount);
|
|
}
|
|
|
|
/* Open the database file
|
|
* path: the full pathname of the file
|
|
*/
|
|
int db_open(char* path)
|
|
{
|
|
char line[255];
|
|
pkginfo_t* info;
|
|
dbfp = fopen(path, "r");
|
|
if(dbfp == NULL) {
|
|
return(1);
|
|
}
|
|
while(!feof(dbfp)) {
|
|
info = (pkginfo_t*)malloc(sizeof(pkginfo_t));
|
|
fgets(line, 64, dbfp);
|
|
if(feof(dbfp)) {
|
|
break;
|
|
}
|
|
strcpy(info->name, trim(line));
|
|
fgets(line, 32, dbfp);
|
|
strcpy(info->version, trim(line));
|
|
/* add to the collective */
|
|
packages = (pkginfo_t**)realloc(packages, (++pkgcount) * sizeof(pkginfo_t*));
|
|
packages[pkgcount-1] = info;
|
|
for(;;) {
|
|
fgets(line, 255, dbfp);
|
|
if(feof(dbfp)) {
|
|
return(2);
|
|
}
|
|
if(strlen(trim(line)) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* Copy the old database file to a backup and build the
|
|
* new copy in its place.
|
|
* files: list of files in the new package, can be null
|
|
* filecount: number of entries in files
|
|
*
|
|
* Returns: 0 on success
|
|
*
|
|
*/
|
|
int db_update(fileset_t files, unsigned int filecount)
|
|
{
|
|
FILE* olddb;
|
|
char* newpath = NULL;
|
|
char* str = NULL;
|
|
char name[64];
|
|
char ver[32];
|
|
char line[255];
|
|
int i = 0, found = 0, done = 0;
|
|
|
|
/* build the backup pathname */
|
|
newpath = (char*)malloc(strlen(dbpath)+5);
|
|
strcpy(newpath, dbpath);
|
|
strcat(newpath, ".bak");
|
|
|
|
/* rename the existing db */
|
|
fclose(dbfp);
|
|
rename(dbpath, newpath);
|
|
|
|
olddb = fopen(newpath, "r");
|
|
free(newpath);
|
|
dbfp = fopen(dbpath, "w");
|
|
if(olddb == NULL || dbfp == NULL) {
|
|
return(1);
|
|
}
|
|
|
|
rewind(olddb);
|
|
while(!feof(olddb)) {
|
|
if(!fgets(name, 64, olddb)) {
|
|
break;
|
|
}
|
|
strcpy(name, trim(name));
|
|
fgets(ver, 32, olddb);
|
|
found = 0;
|
|
for(i = 0; i < pkgcount && !found; i++) {
|
|
if(!strcmp(packages[i]->name, name)) {
|
|
/* it's there... copy the entries over */
|
|
found = 1;
|
|
fputs(name, dbfp);
|
|
fputc('\n', dbfp);
|
|
fputs(ver, dbfp);
|
|
for(done = 0; !done;) {
|
|
fgets(line, 255, olddb);
|
|
if(found) {
|
|
fputs(line, dbfp);
|
|
}
|
|
if(strlen(trim(line)) == 0 || feof(olddb)) {
|
|
done = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(!found) {
|
|
/* skip through filelist for this package */
|
|
for(done = 0; !done;) {
|
|
fgets(line, 255, olddb);
|
|
if(strlen(trim(line)) == 0 || feof(olddb)) {
|
|
done = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(olddb);
|
|
|
|
/* write the current info */
|
|
if(files != NULL) {
|
|
fputs(pkgname, dbfp);
|
|
fputc('\n', dbfp);
|
|
fputs(pkgver, dbfp);
|
|
fputc('\n', dbfp);
|
|
for(i = 0; i < filecount; i++) {
|
|
str = files[i];
|
|
str += strlen(pmo_root);
|
|
if(files[i][0] == '*') {
|
|
fputc('*', dbfp);
|
|
str++;
|
|
}
|
|
fputs(str, dbfp);
|
|
fputc('\n', dbfp);
|
|
}
|
|
fputs("\n", dbfp);
|
|
}
|
|
fclose(dbfp);
|
|
db_open(dbpath);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* Check the database for conflicts
|
|
* files: list of files in the new package
|
|
* filecount: number of entries in files
|
|
*
|
|
* Returns: 0 if no conflicts were found, 1 otherwise
|
|
*
|
|
*/
|
|
int db_find_conflicts(fileset_t files, unsigned int filecount)
|
|
{
|
|
int i;
|
|
char line[255];
|
|
char name[255];
|
|
char* dbstr = NULL;
|
|
char* filestr = NULL;
|
|
struct stat buf;
|
|
int conflicts = 0;
|
|
|
|
/* CHECK 1: checking db conflicts */
|
|
rewind(dbfp);
|
|
while(!feof(dbfp)) {
|
|
fgets(name, 255, dbfp);
|
|
strcpy(name, trim(name));
|
|
if(!pmo_upgrade && !strcmp(name, pkgname)) {
|
|
printf("error: This package is already installed.\n");
|
|
printf(" Maybe you should be using --upgrade.\n");
|
|
return(2);
|
|
}
|
|
fgets(line, 255, dbfp);
|
|
while(!feof(dbfp)) {
|
|
fgets(line, 255, dbfp);
|
|
strcpy(line, trim(line));
|
|
dbstr = line;
|
|
if(dbstr[0] == '*') {
|
|
dbstr++;
|
|
}
|
|
if(!strlen(dbstr)) {
|
|
break;
|
|
}
|
|
if(index(dbstr, '/') == dbstr && (!pmo_upgrade || strcmp(name,pkgname))) {
|
|
for(i = 0; i < filecount; i++) {
|
|
filestr = files[i];
|
|
if(filestr[0] == '*') {
|
|
filestr++;
|
|
}
|
|
if(!strcmp(dbstr, filestr)) {
|
|
if(rindex(files[i], '/') == filestr+strlen(filestr)-1) {
|
|
/* this filename has a trailing '/', so it's a directory -- skip it. */
|
|
continue;
|
|
}
|
|
printf("conflict: %s already exists in package \"%s\"\n", dbstr, name);
|
|
conflicts = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* CHECK 2: checking filesystem conflicts */
|
|
/* TODO: run filesystem checks for upgrades */
|
|
for(i = 0; i < filecount && !pmo_upgrade; i++) {
|
|
filestr = files[i];
|
|
if(filestr[0] == '*') {
|
|
filestr++;
|
|
}
|
|
if(!stat(filestr, &buf) && !S_ISDIR(buf.st_mode)) {
|
|
printf("conflict: %s already exists in filesystem\n", filestr);
|
|
conflicts = 1;
|
|
}
|
|
}
|
|
|
|
return(conflicts);
|
|
}
|
|
|
|
/* Parses the package description file for the current package
|
|
* descfile: the full pathname of the description file
|
|
*
|
|
* Returns: 0 on success, 1 on error
|
|
*
|
|
*/
|
|
int parse_descfile(char* descfile, unsigned short output, fileset_t *bakptr,
|
|
unsigned int* bakct)
|
|
{
|
|
FILE* fp;
|
|
char line[255];
|
|
char* ptr = NULL;
|
|
char* key = NULL;
|
|
int linenum = 0;
|
|
fileset_t backup = NULL;
|
|
unsigned int count = 0;
|
|
|
|
if((fp = fopen(descfile, "r")) == NULL) {
|
|
perror(descfile);
|
|
return(1);
|
|
}
|
|
|
|
while(!feof(fp)) {
|
|
fgets(line, 255, fp);
|
|
if(output) {
|
|
printf("%s", line);
|
|
}
|
|
linenum++;
|
|
strcpy(line, trim(line));
|
|
if(index(line, '#') == (char*)line) {
|
|
continue;
|
|
}
|
|
if(strlen(line) == 0) {
|
|
continue;
|
|
}
|
|
ptr = line;
|
|
key = strsep(&ptr, "=");
|
|
if(key == NULL || ptr == NULL) {
|
|
printf("Syntax error in description file line %d\n", linenum);
|
|
} else {
|
|
key = trim(key);
|
|
key = strtoupper(key);
|
|
ptr = trim(ptr);
|
|
if(!strcmp(key, "PKGNAME")) {
|
|
pkgname = (char*)malloc(strlen(ptr)+1);
|
|
strcpy(pkgname, ptr);
|
|
} else if(!strcmp(key, "PKGVER")) {
|
|
pkgver = (char*)malloc(strlen(ptr)+1);
|
|
strcpy(pkgver, ptr);
|
|
} else if(!strcmp(key, "PKGDESC")) {
|
|
/* Not used yet */
|
|
} else if(!strcmp(key, "BACKUP")) {
|
|
backup = (fileset_t)realloc(backup, (++count) * sizeof(char*));
|
|
backup[count-1] = (char*)malloc(strlen(ptr)+1);
|
|
strcpy(backup[count-1], ptr);
|
|
} else {
|
|
printf("Syntax error in description file line %d\n", linenum);
|
|
}
|
|
}
|
|
line[0] = '\0';
|
|
}
|
|
fclose(fp);
|
|
unlink(descfile);
|
|
|
|
if(count > 0) {
|
|
(*bakptr) = backup;
|
|
(*bakct) = count;
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* Parse command-line arguments for each operation
|
|
* op: the operation code requested
|
|
* argc: argc
|
|
* argv: argv
|
|
*
|
|
* Returns: the functional variable for that operation
|
|
* (eg, the package file name for PM_ADD)
|
|
*/
|
|
char* parseargs(int op, int argc, char** argv)
|
|
{
|
|
char* pkg = NULL;
|
|
int i;
|
|
|
|
for(i = 2; i < argc; i++) {
|
|
if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
|
|
pmo_nofunc = 1;
|
|
usage(op, (char*)basename(argv[0]));
|
|
return(NULL);
|
|
} else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) {
|
|
pmo_verbose = 1;
|
|
} else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--force")) {
|
|
pmo_force = 1;
|
|
} else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--root")) {
|
|
i++;
|
|
if(i >= argc) {
|
|
printf("error: missing argument for %s\n", argv[i-1]);
|
|
return(NULL);
|
|
}
|
|
free(pmo_root);
|
|
pmo_root = (char*)malloc(PATH_MAX);
|
|
if(realpath(argv[i], pmo_root) == NULL) {
|
|
perror("bad root path");
|
|
return(NULL);
|
|
}
|
|
} else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--nosave")) {
|
|
pmo_nosave = 1;
|
|
} else if(!strcmp(argv[i], "-o") || !strcmp(argv[i], "--owns")) {
|
|
/* PM_QUERY only */
|
|
i++;
|
|
if(i >= argc) {
|
|
printf("error: missing argument for %s\n", argv[i-1]);
|
|
return(NULL);
|
|
}
|
|
free(pmo_q_owns);
|
|
pmo_q_owns = (char*)malloc(PATH_MAX);
|
|
if(realpath(argv[i], pmo_q_owns) == NULL) {
|
|
perror("bad path specified for --owns");
|
|
pmo_nofunc = 1;
|
|
return(NULL);
|
|
}
|
|
} else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--list")) {
|
|
/* PM_QUERY only */
|
|
pmo_q_list = 1;
|
|
} else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--file")) {
|
|
/* PM_QUERY only */
|
|
pmo_q_isfile = 1;
|
|
} else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--info")) {
|
|
/* PM_QUERY only */
|
|
pmo_q_info = 1;
|
|
} else {
|
|
pkg = argv[i];
|
|
}
|
|
}
|
|
|
|
if(op != PM_QUERY && pkg == NULL) {
|
|
printf("error: no package specified\n\n");
|
|
usage(op, (char*)basename(argv[0]));
|
|
return(NULL);
|
|
}
|
|
if(op == PM_QUERY && pmo_q_isfile && pkg == NULL) {
|
|
printf("error: no package file specified\n\n");
|
|
return(NULL);
|
|
}
|
|
return(pkg);
|
|
}
|
|
|
|
/* Display usage/syntax for the specified operation.
|
|
* op: the operation code requested
|
|
* myname: basename(argv[0])
|
|
*/
|
|
void usage(int op, char* myname)
|
|
{
|
|
if(op == PM_MAIN) {
|
|
printf("usage: %s {-h --help}\n", myname);
|
|
printf(" %s {-V --version}\n", myname);
|
|
printf(" %s {-A --add} [options] <file>\n", myname);
|
|
printf(" %s {-R --remove} [options] <package>\n", myname);
|
|
printf(" %s {-U --upgrade} [options] <file>\n", myname);
|
|
printf(" %s {-Q --query} [options] [package]\n", myname);
|
|
} else if(op == PM_ADD) {
|
|
printf("usage: %s {-A --add} [options] <file>\n", myname);
|
|
printf("options:\n");
|
|
printf(" -f, --force force install, overwrite conflicting files\n");
|
|
printf(" -n, --nosave do not save configuration files\n");
|
|
printf(" -v, --verbose be verbose\n");
|
|
printf(" -r, --root <path> set an alternative installation root\n");
|
|
} else if(op == PM_REMOVE) {
|
|
printf("usage: %s {-R --remove} [options] <package>\n", myname);
|
|
printf("options:\n");
|
|
printf(" -n, --nosave do not save configuration files\n");
|
|
printf(" -v, --verbose be verbose\n");
|
|
printf(" -r, --root <path> set an alternative installation root\n");
|
|
} else if(op == PM_UPGRADE) {
|
|
printf("usage: %s {-U --upgrade} [options] <file>\n", myname);
|
|
printf("options:\n");
|
|
printf(" -f, --force force install, overwrite conflicting files\n");
|
|
printf(" -n, --nosave do not save configuration files\n");
|
|
printf(" -v, --verbose be verbose\n");
|
|
printf(" -r, --root <path> set an alternative installation root\n");
|
|
} else if(op == PM_QUERY) {
|
|
printf("usage: %s {-Q --query} [options] [package]\n", myname);
|
|
printf("options:\n");
|
|
printf(" -r, --root <path> set an alternative installation root\n");
|
|
printf(" -o, --owns <file> query the package that owns <file>\n");
|
|
printf(" -l, --list list the contents of the queried package\n");
|
|
printf(" -i, --info output the .PKGINFO file (only used with -p)\n");
|
|
printf(" -p, --file if used, then [package] will be the path\n");
|
|
printf(" to an (uninstalled) package to query\n");
|
|
}
|
|
}
|
|
|
|
/* Version
|
|
*/
|
|
void version(void)
|
|
{
|
|
printf("\n");
|
|
printf(" .--. Pacman v%s\n", VERSION);
|
|
printf("/ _.-' .-. .-. .-. Copyright (C) 2002 Judd Vinet <jvinet@zeroflux.org>\n");
|
|
printf("\\ '-. '-' '-' '-' \n");
|
|
printf(" '--' This program may be freely redistributed under\n");
|
|
printf(" the terms of the GNU GPL\n\n");
|
|
}
|
|
|
|
/* Check verbosity option and, if set, print the
|
|
* string to stdout
|
|
*/
|
|
int vprint(char* fmt, ...)
|
|
{
|
|
va_list args;
|
|
if(pmo_verbose) {
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
va_end(args);
|
|
fflush(stdout);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* See if a file should be backed up or not
|
|
*/
|
|
int needbackup(char* file, fileset_t files, unsigned int filect)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < filect; i++) {
|
|
if(files[i][0] == '*' && !strcmp((char*)(files[i]+1), file)) {
|
|
return(1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Test for existence of a string in a fileset
|
|
*/
|
|
int is_in(char* needle, fileset_t haystack, unsigned int hayct)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < hayct; i++) {
|
|
if(!strcmp(haystack[i], needle)) {
|
|
return(1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Convert a string to uppercase
|
|
*/
|
|
char* strtoupper(char* str)
|
|
{
|
|
char* ptr = str;
|
|
|
|
while(*ptr) {
|
|
(*ptr) = toupper(*ptr);
|
|
ptr++;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
/* Trim whitespace and newlines from a string
|
|
*/
|
|
char* trim(char* str)
|
|
{
|
|
char* start = str;
|
|
char* end = str + strlen(str);
|
|
int mid = 0;
|
|
char ch;
|
|
|
|
while(mid < 2) {
|
|
if(!mid) {
|
|
ch = *start;
|
|
if(ch == 10 || ch == 13 || ch == 9 || ch == 32) {
|
|
start++;
|
|
} else {
|
|
mid = 1;
|
|
}
|
|
} else {
|
|
end--;
|
|
ch = *end;
|
|
if(ch == 10 || ch == 13 || ch == 9 || ch == 32) {
|
|
*end = '\0';
|
|
} else {
|
|
mid = 2;
|
|
}
|
|
}
|
|
}
|
|
return(start);
|
|
}
|
|
|
|
/* vim: set ts=2 noet: */
|