UsR2usr (libLQ version)
Posted 04-15-2012 at 04:42 PM by rainbowsally
Here's UsR2usr version 1.5 (using libLQ)
file: src/Usr2usr.cpp
purpose: allows dummy installations and renaming of paths and rpaths for creating binary installations without committing to overwriting original files until you're ready.
file: src/version.h
file: src/usage_str.dat
[If you use mc to create the makefile start with the LQ++ template: 'mc2 -fetch LQ++' and edit the OUTNAME = UsR2usr.]
file: src/Usr2usr.cpp
purpose: allows dummy installations and renaming of paths and rpaths for creating binary installations without committing to overwriting original files until you're ready.
Code:
// UsR2usr.cpp
#include <stdio.h> // printf, sprintf, etc.
#include <stdlib.h> // exit()
#include <unistd.h> // tons of stuff
#include <malloc.h> // memory allocation
#include <string.h> // strings and block comparisons and moves
#include <ctype.h>
#include "version.h"
#include <LQ/slist.h>
void dbg() {
} // a breakpoint for kdbg.exec
// simplified syntax for comparing and moving str/mem
// case insensitive
#define streq(a, b) (strcasecmp(a, b) == 0)
// case sensitive
#define memeq(buf, str) \
(memcmp(buf, str, strlen(str)) == 0)
// move a string minus terminator
#define memstrcpy(buf, str) \
memcpy(buf, str, strlen(str))
enum {
FILETYPE_ELF = 101,
FILETYPE_TEXT,
// others...
FILETYPE_BINARY
};
static char errmsg[256];
typedef struct {
char *buf;
size_t buflen;
bool armed_flag; // = 0;
bool etc_flag; // = 0;
bool elf_only_flag; // = 0;
bool pass_filter;
int filetype; // elf, text, other binary..
int scan_number; // = 0; // true if first file in scan
const char *filename;
} VARS;
VARS *global_vars;
// #define obj (*global_vars)
VARS *vars_new() {
VARS *vars = (VARS *) malloc(sizeof(VARS));
memset(vars, 0, sizeof(VARS));
}
void vars_delete(VARS ** po) {
// destructor
if(*po)
free(*po);
*po = 0;
}
void obj_setPassFilter(VARS * o);
void obj_setFiletype(VARS * o);
// returns count of replacements proposed
int obj_scanAndReplace(VARS * o);
// returns 0 on success, else fatal
int obj_processFile(VARS * o, const char *filename);
const char *appname;
int usage(int errcode);
int show_version(int errcode);
int do_errmsg(int errcode, const char *msgfmt, const char *errvar);
int reorder_args(int argc, char **argv);
int main(int argc, char **argv) {
dbg();
appname = basename(argv[0]);
int arg_offset = reorder_args(argc, argv);
if(argc == 1)
return usage(1);
if(streq(argv[1], "--help"))
return usage(0);
VARS *o = vars_new();
for(int i = 1; i <= arg_offset; i++) {
if(streq(argv[i], "--armed"))
o->armed_flag = 1;
else if(streq(argv[i], "--etc"))
o->etc_flag = 1;
else if(memeq(argv[i], "--elf")) // --elf | --elf-only
o->elf_only_flag = 1;
else if(streq(argv[i], "-v") || streq(argv[i], "--version"))
return show_version(0);
else {
fprintf(stderr, "Unknown switch '%s'\n", argv[i]);
exit(1);
}
}
const char *dirname = argv[1 + arg_offset];
// get list of files in dirname
char tmpbuf[4096];
char **filelist = slist_new();
int numfiles;
sprintf(tmpbuf, "find %s -type f", dirname);
slist_pipeRead(&filelist, tmpbuf);
numfiles = slist_count(filelist);
// tell type of scan
if(numfiles > 0) {
printf("\n");
sprintf(tmpbuf, "Scan type selected = [%s, %s]",
o->armed_flag ? "ARMED" : "check",
o->elf_only_flag ? "ELF only" : "all");
printf("%s\n", tmpbuf);
for(int i = 0; i < strlen(tmpbuf); i++)
printf("-");
printf("\n");
} else {
printf("%s: No files!\n", appname);
return 1;
}
for(int i = 0; i < numfiles; i++) {
const char *filename = filelist[i];
int fatal = obj_processFile(o, filename);
if(fatal)
return 1;
}
vars_delete(&o);
slist_delete(&filelist);
return 0;
}
int show_version(int errcode) {
printf("UsR2usr version %s", VERSION);
return errcode;
}
int do_errmsg(int errcode, const char *msgfmt, const char *errvar) {
if(errvar == 0)
errvar = (char *)"";
sprintf(errmsg, msgfmt, errvar);
perror(errmsg);
return errcode;
}
int usage(int errcode) {
#include "usage_str.dat" // has one %s format for appname
// test for presence of kdialog for the usage message
char syscmd[4096];
sprintf(syscmd, "kdialog -v >/dev/null 2>&1");
int err = system(syscmd);
if(err)
printf(usage_str, appname);
else {
char msg[4096];
sprintf(msg, usage_str, appname);
sprintf(syscmd, "kdialog -msgbox \"%s\" -title \"Usage \"",
msg);
err = system(syscmd);
if(err)
printf(usage_str, appname);
}
return errcode;
}
int reorder_args(int argc, char **argv) {
// make literal copy of argv in temporary list
char **tmp_args = (char **)malloc(argc * sizeof(char **));
memcpy(tmp_args, argv, argc * sizeof(char **));
int cnt = 1; // current copy back number, app path is at [0] still
int nswitches = 0; // the value to return
// copy switches back first
for(int i = 1; i < argc; i++)
if(tmp_args[i][0] == '-')
argv[cnt++] = tmp_args[i];
nswitches = cnt - 1; // number of actual switches
// copy non-switches back next
for(int i = 1; i < argc; i++)
if(tmp_args[i][0] != '-')
argv[cnt++] = tmp_args[i];
free(tmp_args);
return nswitches;
}
void obj_setFiletype(VARS * o) {
do {
if(memeq(o->buf + 1, "ELF"))
printf("");
if(memeq(o->buf, "\177ELF"))
printf("");
if(o->buflen >= 4) {
if(memeq(o->buf, "\177ELF")) {
o->filetype = FILETYPE_ELF;
break;
}
}
int testlen = 32;
int ascii_flag = 1;
if(o->buflen < testlen)
testlen = o->buflen;
for(int i = 0; i < testlen; i++) {
if(!isascii(o->buf[i])) {
ascii_flag = 0;
break;
}
}
if(ascii_flag && (testlen > 0)) {
o->filetype = FILETYPE_TEXT;
break;
}
// other checks..
// default
o->filetype = FILETYPE_BINARY;
} while(0);
}
// returns 0 on success, else fatal
int obj_processFile(VARS * o, const char *filename) {
o->filename = filename;
FILE *fp = fopen(filename, "rb+");
if(!fp)
return do_errmsg(1, "Can't open file '%s'", o->filename);
fseek(fp, 0, SEEK_END);
o->buflen = ftell(fp);
fseek(fp, 0, SEEK_SET);
o->buf = (char *)malloc(o->buflen);
if(!o->buf)
return do_errmsg(1, "Can't allocate memory", 0);
fread(o->buf, 1, o->buflen, fp);
// check file type and set accessible flag to indicate
// whether this is selected to be modified or not.
obj_setFiletype(o);
obj_setPassFilter(o);
// scan for text /UsR or UsR/ and replace with
// /usr or usr/
// save file only if not just checking and
// filetype matches user selection
if(o->pass_filter) {
int count = obj_scanAndReplace(o);
if(count < 0)
return do_errmsg(1, "Can't scan file '%s'", o->filename);
int nbytes; // for debugger
if(o->armed_flag) {
//fseek(fp, 0, SEEK_SET);
fseek(fp, 0, SEEK_SET);
nbytes = fwrite(o->buf, 1, o->buflen, fp);
}
}
fclose(fp);
free(o->buf);
return 0;
}
void obj_dumpContext(VARS * o, char *bufptr, bool change) {
if(!o->scan_number)
printf("\n");
o->scan_number += 1;
// hex dump 16 bytes of context
unsigned char *cptr;
int dumplen = 16;
cptr = (unsigned char *)bufptr - 6;
if((char *)cptr < o->buf)
cptr = (unsigned char *)o->buf;
char *bufend = o->buf + o->buflen;
if(cptr > (unsigned char *)bufend)
dumplen = (unsigned char *)bufend - cptr;
if(!change)
printf("NOT ");
printf("To change in '%s' at offset 0x%0X:\n", o->filename,
bufptr - o->buf);
for(int i = 0; i < dumplen; i++) {
printf("%0.2X ", cptr[i]);
}
printf(" %s\"...", change ? "CHG: \x1b[1;31m" : "---- \x1b[37m");
for(int i = 0; i < dumplen; i++) {
if(isprint(cptr[i]))
printf("%c", cptr[i]);
else
printf(".");
}
printf("...\"\x1b[0;30m\n\n");
}
void obj_showStatus(VARS * o, char *bufptr, bool change) {
if(o->armed_flag) {
printf("Found 'UsR' in '%s' at 0x%X %s\n", o->filename,
bufptr - o->buf, change ? "(CONVERTED)" : "(No Change)");
} else {
obj_dumpContext(o, bufptr, change);
}
}
// returns number of changes made.
int obj_scanAndReplace(VARS * o) {
int count = 0;
char *ptr = o->buf;
char *bufend = ptr + o->buflen;
int found;
found = 0;
while(ptr < bufend) {
found = 0; // flag found in context
const char *replstr = 0; // flag and string to skip
do {
// UsR in correct context
// check for any all at once
if(strchr("/U\'\"", *ptr)) {
if(*ptr == 'U') {
static const char *this_str = "UsR/";
if(memeq(ptr, this_str)) {
found++;
replstr = "usr/";
} else if(memeq(ptr, "UsR")) {
replstr = "UsR";
}
} else if(*ptr == '/') {
static const char *this_str = "/UsR";
if(memeq(ptr, this_str)) {
found++;
replstr = "/usr";
}
} else if(*ptr == '\'') {
static const char *this_str = "\'UsR\'";
if(memeq(ptr, this_str)) {
found++;
replstr = "\'usr\'";
}
} else if(*ptr == '\"') {
static const char *this_str = "\"UsR\"";
if(memeq(ptr, this_str)) {
found++;
replstr = "\"usr\"";
}
}
} // if(strchr
// found & show copy count steps
// | (replstr & steps | 1step)
if(found) {
obj_showStatus(o, ptr, true); // and show 'change'
memstrcpy(ptr, replstr);
ptr += strlen(replstr);
count++;
} else if(o->etc_flag) {
// same thing but for EtC to etc
if(strchr("/E\'\"", *ptr)) {
if(*ptr == 'E') {
static const char *this_str = "EtC/";
if(memeq(ptr, this_str)) {
found++;
replstr = "etc/";
} else if(memeq(ptr, "EtC")) {
replstr = "etc";
}
} else if(*ptr == '/') {
static const char *this_str = "/EtC";
if(memeq(ptr, this_str)) {
found++;
replstr = "/etc";
}
} else if(*ptr == '\'') {
static const char *this_str = "\'EtC\'";
if(memeq(ptr, this_str)) {
found++;
replstr = "\'etc\'";
}
} else if(*ptr == '\"') {
static const char *this_str = "\"EtC\"";
if(memeq(ptr, this_str)) {
found++;
replstr = "\"etc\"";
}
}
} // if(strchr
if(found) {
obj_showStatus(o, ptr, true); // and show 'change'
memstrcpy(ptr, replstr);
ptr += strlen(replstr);
count++;
}
}
if(!found) // not found
{
if(replstr) {
obj_showStatus(o, ptr, false); // show 'no change'
ptr += strlen(replstr);
} else
ptr++; // step 1
}
} while(0); // do
} // while(bufptr...)
return count;
}
void obj_setPassFilter(VARS * o) {
// if filetype = elf or --all then true else false
o->pass_filter = false;
if(o->filetype == FILETYPE_ELF)
o->pass_filter = true;
else if(!o->elf_only_flag)
o->pass_filter = true;
}
Code:
// created // #define VERSION "1.0" // added '--check' option, tweeked help and more options and added version printout // #define VERSION "1.1" // minor changes. // #define VERSION "1.2" // Feb 2, 2012 // added gui-based help if supported, made vars an 'object' for better // debug view, etc. // #define VERSION "1.3.0" // Feb 9, 2012 // added /EtC to conversions for stuff like gtk+ // added warn if info/dir is present in share/info directory. // #define VERSION "1.4.0" // Apr 15, 2012 // converted to use libLQ slist funcs #define VERSION "1.5.0"
Code:
/* usage_str.txt converted with txt2cstr */ const char *usage_str = "Usage: %s [switches] dir\n" "\n" " For use with 'configure' based installations to install into\n" " prefix=/UsR and then converted to a binary installation into the\n" " real /usr folder.\n" "\n" " How: \n" " This application scans and replaces the path UsR found in elf\n" " binaries and other files allowing installation into /UsR and\n" " then changing the path to a real system directory.\n" "\n" " Several checks are performed on the text before committing to\n" " a change and it can be first (without the --armed switch( which \n" " you can verify in a hex viewers before creating the binary \n" " installation. \n" "\n" " Is it fool proof? Pretty close, but some caution is advised.\n" " Keeping a backup of folders and doing a test installation on\n" " your own system to see what changes is just good sense and as\n" " usual, no warantees at this end. Just best wishes and a bit\n" " of optimism considering the alternatives which you might want\n" " to check out if there are any.\n" "\n" " Switches:\n" " [none] - prints file names and offsets where /UsR is\n" " found and set to be changed in an armed run.\n" " Shows a bit of context (hex/ascii dump) and\n" " can also be used to view in a hex editor.\n" "\n" " Includes text files and other other binaries\n" " in the scan in addition to elf binaries (the\n" " default).\n" "\n" " --etc Adds /EtC to /etc conversion for installations \n" " that need --infodir=/EtC to keep them from \n" " clobbering installed files.\n" "\n" " --armed - commit to changes reported in dry run (default).\n" "\n" " --elf | --elf-only\n" " - Only change rpaths and other occurrances of \n" " UsR in a path context in elf binaries (libs\n" " and executables).\n" "\n" " Tips: \n" " * After intalling into prefix=/UsR, tar up the /UsR directory\n" " where it is before moving the files to where you will build\n" " your package to avoid broken links and missing files, etc.\n" "\n" " * If after running with no switches and none of the non-elf files\n" " need to be modified, you can add the '--elf' switch for the \n" " armed run.\n" "\n" " * For use as a non-root user, any directory tree is assumed to \n" " have come from and is intended to be installed at root. You \n" " may run this before or after having moved the files. Don't\n" " run it in the source though. :-)\n" "\n" " Note: The /UsR (prefix) folder you install into using the initial\n" " compilation and installation must be located at the root of the\n" " file system. The number of letters in the path is critical to\n" " this application's ability to accurately modify 'rpath' and other\n" " text after you have installed into the /UsR dummy directory.\n" "\n" " Copyright (C) 2012, Rainbow Sally\n" "\n" " Released under the GPL license.\n" "\n";
Total Comments 0




