
/*  $Id: dtrt.c,v 1.8 1995/02/02 11:53:06 yoav Exp yoav $  */

/*
*
*   okay silly program to do whatever is defined in a magic file on 
*      a file passed in the commandline (only one file) or via
*      stdin
*/

#ifdef POSIX
#  include <unistd.h>
#else
#  ifndef STDIN_FILENO
#    define STDIN_FILENO 0
#  endif
#endif

#include <Xm/MainW.h>
#include <Xm/LabelG.h>
#include "xcp.h"

void execute(char *, char *), resolve_name(), display(), get_filename(), get_path(),
     fix(), get_home(), switch_disp(), exit_well();
Boolean getfiletype(char *, char *), getcommand(char *, char *);

/*  okay.. so i like globals...  */
Widget browser_main, browser_pane, browser_name, browser_text, 
       browser_done, buttons_form, browser_switch, toplevel;
XtAppContext app;
int length;
char *ascii = NULL, *hex = NULL, *orig = NULL;
XmString hex_string, ascii_string;
String fallbacks[] = {
  "dtrt*foreground: gray15",
  "dtrt*background: gray70",
  NULL};

/* done with globals  */

main(int argc, char **argv)
{
  char type[MAXTYPELEN];
  char command[MAXCOMMANDLEN];
  FILE *source;
  int pipefd[2] = {-1, -1};
  char filename[MAXPATHLEN]="";

  toplevel = XtVaAppInitialize (&app, "dtrt", NULL, 0, &argc, argv, fallbacks, NULL);

  if (argc > 2){
    fprintf(stderr, "USAGE: dtrt [filename]\n");
    exit(1);
  }
  if ((argc != 1) && (strcmp(argv[1], "-"))){
    if (!strcmp(argv[0], argv[1])){
      fprintf(stderr, "You REALLY don't want to do this!!! \n");
      exit(1);
    }
    /* try to exec it first.. if that fails it's not executable */
    execl(argv[1], argv[1], 0);
    /* exec failed, not executable / stdin*/
  }
  if (getfiletype(argv[1], type)){
    if (strcmp(type, "(junk)")){    /*  not that keyword   */
      if (getcommand(type, command)){
	if (command[0]==NULL){
	  if ((argc != 1) && (strcmp(argv[1], "-"))){
	    if (pipe(pipefd) == 0){
	      perror("pipe");
	      exit(1);
	    }
	    if (dup2(pipefd[1], STDIN_FILENO) != STDIN_FILENO)
	      perror("dup2");
	  }
	}
	else {
	  execute(command, argv[1]);
	  perror("execute returned:");
	  exit(1);    /*  bug ??  */
	}
      }
    }
    else{
      printf("It's a: %s file, but i don't know what to do\n", type); 
      exit(0);
    }
  }
  display(argv[1], argc, argv);
}
  
Boolean getfiletype(char *source, char *type)
{
  FILE *magic, *source_file;
  char string_buf[MAXPATHLEN], current_magic[MAXMAGICLENGTH+1];
  Boolean cont = False;
  int offset, f_type, desc_len;
  char *temp;
  char compare[MAXMAGICLENGTH+1];
  long long_value, long_source;
  char magic_type[MAXMAGICTYPE], desc[MAXMAGICDESC], result[MAXMAGICRESULT];

  if ((magic = fopen (MAGICFILE, "r")) == NULL){
    fprintf(stderr, "Can't read magic file :%s\n", MAGICFILE);
    return(False);
  }
  if (source[0] != NULL)
    if ((source_file = fopen (source, "r")) == NULL){
      if ((source_file = fdopen (STDIN_FILENO, "r")) == NULL){
	fprintf(stderr, "Can't read from standard input");
	return(False);
      }
    }
  cont = False;    /*  hopefully the first letter in the magicfile isn't a >  */

  while (!feof(magic)){
    if (fgets(current_magic, MAXMAGICLENGTH, magic)){
      if ((current_magic[0]!='#') && (current_magic[0]!='\n')){
	if (cont && (current_magic[0]!='>')){
	  fclose(magic);
	  return(True);    /*  already set... no extension  */
	}
	if (((current_magic[0]=='>') && cont) || (current_magic[0]!='>')){
	  if (cont)
	    sscanf(current_magic, ">%d%s%s%s\n", &offset, magic_type, desc, result);
	  else
	    if (current_magic[0] == '*'){
	      sscanf(current_magic, "*%s%s%s\n", magic_type, desc, result);
	      if (!strcmp(magic_type, "filename"))
		f_type = FILENAME;
	      else if (!strcmp(magic_type, "extension"))
		f_type = EXTENSION;
	      else if (!strcmp(magic_type, "prefix"))
		f_type = PREFIX;
	      else
		f_type = ~FILENAME;
	      switch(f_type){
	      case FILENAME:
		if (!strcmp(source, desc)){
		  strcpy(type, result);
		  cont = True;
		}
		break;
	      case EXTENSION:
		temp = strlen(source) + source;  /*  temp points to NULL terminator  */
		temp = temp - strlen(desc); /* temp points to start of description */
		if (!strcasecmp(desc, temp)){
		  strcpy(type, result);
		  cont = True;
		}
		break;
	      case PREFIX:
		if (!strncasecmp(source, desc, strlen(desc))){
		  strcpy(type,result);
		  cont=True;		 
		}
		break;
	      default:
		fprintf(stderr, "Oh shit!!\n");
		exit(1);
	      }
	    }
	    else{
	      sscanf(current_magic, "%d%s%s%s\n", &offset, magic_type, desc, result);
	      if (fseek(source_file, offset, SEEK_SET) != -1){
		if (!strcmp(magic_type, "string")){
		  f_type=STRING;
		  fix(desc, &desc_len);  /*  change /023 into one char  */
		}
		else if (!strcmp(magic_type, "byte"))
		  f_type=BYTE;
	      else if (!strcmp(magic_type, "short"))
		f_type=SHORT;
	      else if (!strcmp(magic_type, "long"))
		f_type=LONG;
	      else
		f_type = ~STRING; /*  this should never happen  */
	      switch(f_type){
	      case STRING:
		if (fread(string_buf, 1, desc_len, source_file)){
		  if (!memcmp(string_buf, desc, desc_len)){
		    strcpy (type, result);
		    cont = True;
		  }
		}
		break;
	      case BYTE:
		long_value = strtol(desc, (char **)NULL, 0);
		if (fread(&long_source, sizeof(unsigned char), 1, source_file)){
		  long_source &= 0xff; /*  zero out the other elements  */
		  if (long_source == long_value){
		    strcpy(type, result);
		    cont=True;
		  }
		}
		break;
	      case LONG:
		long_value=strtol(desc, (char **)NULL, 0);
		fread(&long_source, (sizeof (long)), 1, source_file);
		if (long_source==long_value){
		  strcpy(type, result);
		  cont=True;
		}
		break;
	      case SHORT:
		long_value=strtol(desc, (char **)NULL, 0);
		fread(&long_source, (sizeof(short)), 1, source_file);
		long_source &= 0xffff; /*  zero out the other elements  */
		if (long_source==long_value){
		  strcpy(type, result);
		  cont=True;
		}
		break;
	      default:
		/*  WTF??  */
		printf("%s, %s, %s\n", magic_type, desc, result);
		fprintf(stderr, "Problem with your magic file");
		fclose(magic);
		fclose(source_file);
		return(False);
	      }
	      }
	    }
	}
      }
    }
  }
  fclose(source_file);
  fclose(magic);
  if (cont)
    return(True);
  else
    return(False);
}

Boolean getcommand(char *type, char *command)
{
  FILE *commandfile;
  char name[MAXMAGICRESULT], curr[MAXCOMMANDFILELEN];
  char result[MAXCOMMAND];
  char *temp;
  Boolean okay;
  int position;

  get_home(result);
  strcat(result, "/.dtrtrc");
  if ((commandfile = fopen (result, "r")) == NULL)
    if ((commandfile = fopen (COMMANDFILE, "r")) == NULL){
      fprintf(stderr, "Can't find the commandfile :%s\n");
      return(False);
    }
  while(!feof(commandfile)){
    if (fgets(curr, MAXCOMMANDFILELEN, commandfile))
      if ((curr[0] != '#') && (curr[0] != '\n') && (curr[0] != '<')){
	position=0;
	okay = True;
	do{
	  if (type[position] != curr[position])
	    okay = False;
	  else
	    position++;
	} while ((okay) && (type[position]));
		      /* compare type to the first word in the commandfile
		 for example GIF vs. "GIF    /mit/graphics/showgif"  are okay  */
	if (okay)
	  if ((curr[position] != ' ') && (curr[position] != '\t'))
	    okay=False;  /*  GIF  vs "GIFB  /foo/bar"  not good  */
	  else{
	    while ((curr[position] == ' ') || (curr[position] == '\t'))
	      position++;   /*  get rid of white space  */
	    while (temp = strrchr(&curr[position], '\n'))
	      *temp='\0';   /*  get rid of trailing returns  */
	    strcpy(command, &(curr[position]));  /*  copy the command  */
	    while (command[0] == '>'){
/* This is a hack!!  it seems some commands on athena require an attach first...
    so this allows the option of sort of executing more than one command..
    only requirement is that $command$ things are not included until the last command
    this will just "system" all the pre-commands... not perfect.. but it will do...
    basically.. every precommand must have a > as it's first letter...
    every command or precommand after the first should have a < indicating it's
    linked to the previous line (and to make my life easier  :-)
    */	    
	      system(&command[1]);
	      if (fgets(curr, MAXCOMMANDFILELEN, commandfile)){
		if ((curr[0] != '#') && (curr[0] != '\n')){
		  position=0;
		  while ((curr[position] == ' ') || (curr[position] == '\t') ||
			 (curr[position] == '<'))
		    position++;   /*  get rid of white space  */
		  while (temp = strrchr(&curr[position], '\n'))
		    *temp='\0';   /*  get rid of trailing returns  */		  
		  strcpy(command, &(curr[position]));
		}
	      }
	      else{
		fprintf(stderr,"Your command_file is screwed up!\n");
		return(False);
	      }
	    }
	    fclose(commandfile);
	    return(True);
	  }
      }
  }
}

void execute(char *command, char *filename)
{  /*  filename is a full pathname   */

  char **argv=NULL;
  char commandname[MAXCOMMANDLEN];
  char temp1[MAXPATHLEN], temp2[MAXPATHLEN];
  char *t;
  int argvcount = 5, argpointer;

  strcpy(temp1, command);
  t=strtok(temp1, " ");
  while (t){
    if (t[0] == '$'){
#ifdef DEBUG
      printf("%s, %s, ", t, filename);
#endif
      resolve_name(t, filename, temp2);
#ifdef DEBUG
      printf("%s\n", temp2);
#endif
    }
    else
      strcpy(temp2,t);
    if (argv==NULL){
      strcpy(commandname, temp2);
      if ((argv=malloc( argvcount * sizeof(char *))) == NULL){
	perror("malloc");
	return;
      }
      if ((argv[0]=malloc(sizeof(char) *strlen(temp2) +1)) == NULL){
	perror("malloc");
	exit(1);
      }
      get_filename(temp2, argv[0]);
      argpointer=1;
      t=strtok(NULL, " ");
    }
    else{
      if (argpointer == argvcount-2){   /*  need one more spot for the null */
	argvcount=argvcount+5;
	if ((argv=realloc(argv, argvcount * sizeof(char *))) == NULL){
	  perror("realloc");
	  exit(1);
	}
      }
      if ((argv[argpointer] = malloc(sizeof(char) * strlen(temp2) + 1)) == NULL){
	perror("malloc");
	exit(1);
      }
      strcpy(argv[argpointer], temp2);
      argpointer++;
      t=strtok(NULL," ");
    }
  }
  argv[argpointer] = NULL;
  execvp(commandname, argv);
  fprintf(stderr,"can't execute the command required for this: %s\n",
	  commandname);
  exit(0);
}

void resolve_name(template, filename, result)
     char *template, *filename, *result;
{
  if (template[0] != '$'){
    strcpy(result,filename);
    return;
  }
  if ((!strcasecmp(template, "$file$")) || (!strcasecmp(template, "$filename$"))){
    get_filename(filename, result);
    return;
  }
  if ((!strcasecmp(template, "$path$")) || (!strcasecmp(template, "$pathname$"))){
    get_path(filename, result);
    return;
  }
  if ((!strcasecmp(template, "$fullfile$")) || (!strcasecmp(template, "$ffile$"))){
    strcpy(result, filename);
    return;
  }
  fprintf(stderr, " You have a bad code in your commandfile\n");
  strcpy(result, "");
  return;
}

void display(filename, argc, argv)
char *filename;
int argc;
char *argv[];
{
  XmString filename_text;
  XtWidgetGeometry geom;
  Widget filename_widget;
  char buf[MAXPATHLEN+30];
  Arg args[10];
  FILE *fp;
  struct stat statb;
  int tempcount;
  Boolean hex_first=False;

  if (stat(filename, &statb) == -1 ||
      (statb.st_mode & S_IFMT) != S_IFREG ||
      !(fp = fopen(filename, "r"))) {
    if ((statb.st_mode & S_IFMT) == S_IFREG)
      fprintf(stderr, "I don't think i can display this\n");
    else
      return;  /* this is not a regular file  */
  }
  length = statb.st_size + 1;
  if (!(orig = XtMalloc((unsigned)length))) {
    fprintf(stderr,"Can't allocate memory");
    fclose(fp);
    return;
  }
  if (!fread(orig, sizeof(char), length, fp))
    fprintf(stderr, "Warning: may not have read entire file!\n");
  orig[statb.st_size] = 0;
  if (!(hex = XtMalloc((sizeof(char) * 3) * length + 1))){
    perror("XtMalloc");
    fclose(fp);
    return;
  }
  if (!(ascii = XtMalloc((unsigned)length))){
    perror("XtMalloc");
    fclose(fp);
    return;
  }
  for(tempcount=0; tempcount<statb.st_size; tempcount++){
    if (isprint(orig[tempcount]) || (isspace(orig[tempcount])))
      ascii[tempcount]=orig[tempcount];
    else {
      ascii[tempcount]='.';
      hex_first=True;
    }
    sprintf(&hex[tempcount*3], "%02X ", (unsigned char)orig[tempcount]);
    if (!((tempcount+1)%27))
      hex[tempcount*3+2] = '\n';    /*  yuck... add a newline after 27 chars  */
  }
  XtFree(orig);
  fclose(fp);

  browser_main = XtVaCreateManagedWidget("dtrt_form", xmMainWindowWidgetClass, toplevel,
				  NULL);
  browser_pane = XtVaCreateManagedWidget("dtrt_pane", xmPanedWindowWidgetClass,
					   browser_main, 
					   NULL);  
  filename_text = XmStringCreateSimple(filename);
  filename_widget = XtVaCreateManagedWidget("name", xmLabelGadgetClass, browser_pane,
			  XmNalignment, XmALIGNMENT_CENTER,
			  XmNlabelString, filename_text,
			  NULL);
  XmStringFree(filename_text);
  geom.request_mode = CWHeight;
  XtQueryGeometry(filename_widget, NULL, &geom);
  XtVaSetValues (filename_widget,
		 XmNpaneMinimum, geom.height,
		 XmNpaneMaximum, geom.height,
		 NULL);
  XtSetArg(args[0], XmNeditable,  False);
  XtSetArg(args[1], XmNeditMode,  XmMULTI_LINE_EDIT);
  XtSetArg(args[2], XmNwordWrap, True);
  XtSetArg(args[3], XmNvalue, hex_first ? hex : ascii);
  XtSetArg(args[4], XmNscrollHorizontal, False);
  browser_text = XmCreateScrolledText(browser_pane, "scrolltext", args, 5);
  XtManageChild(browser_text);
  hex_string = XmStringCreateSimple("Hex");
  ascii_string = XmStringCreateSimple("ASCII");
  buttons_form = XtVaCreateWidget("buttons", xmFormWidgetClass,
				    browser_pane,
				    NULL);
  filename_text = XmStringCreateSimple("Done");
  browser_done = XtVaCreateManagedWidget("done", xmPushButtonWidgetClass, 
					 buttons_form,
					 XmNlabelString, filename_text,
					 XmNleftAttachment, XmATTACH_POSITION,
					 XmNleftPosition, 20,
					 XmNrightAttachment, XmATTACH_POSITION,
					 XmNrightPosition, 40,
					 XmNresizable, False,
					 NULL);
  XtAddCallback(browser_done, XmNactivateCallback, exit_well, NULL);
  XmStringFree(filename_text);
  browser_switch  = XtVaCreateManagedWidget("switch", xmPushButtonWidgetClass, 
					 buttons_form,
					 XmNlabelString, hex_first ? ascii_string : hex_string,
					 XmNleftAttachment, XmATTACH_POSITION,
					 XmNleftPosition, 60,
					 XmNrightAttachment, XmATTACH_POSITION,
					 XmNrightPosition, 80,
					 XmNresizable, False,
					 NULL);
  XtAddCallback(browser_switch, XmNactivateCallback, switch_disp, NULL);
  XtManageChild(buttons_form);
  geom.request_mode = CWHeight;
  XtQueryGeometry(buttons_form, NULL, &geom);
  XtVaSetValues (buttons_form,
		 XmNpaneMinimum, geom.height,
		 XmNpaneMaximum, geom.height,
		 NULL);
  XtVaSetValues(browser_text, 
		XmNcolumns,   80,
		XmNrows,      40,
		NULL);
  XtRealizeWidget(toplevel);
  XtAppMainLoop(app);
}

void get_filename(name, result)
char *name, *result;
{
  char *temp;
  
  temp = strrchr(name, '/');
  if (temp)
    strcpy(result, temp+sizeof(char));
  else
    strcpy(result, name);
}

void get_path(name, result)
char *name, *result;
{
  int len;
  char *temp;
  
  temp = strrchr(name, '/');
  if (temp){
    len = (temp - name);
    strncpy(result, name, len);
    result[len] = '\0';
  }
  else
    getwd(result);
}

void fix(string, desc_length)
char *string;
int *desc_length;
{
  char temp[MAXMAGICLENGTH];
  int now = 0, looking = 0;
  int value, x;
  
  while (string[looking] != '\0') {
    if (string[looking] != '\\')
      temp[now] = string[looking];
    else
      if (string[looking+1] == '\\')
        temp[now] = '\\';
      else{
        temp[now] = strtol(&string[looking+1], NULL, 8);  /* assuming three chars!!!  */
                                                         /* bad assumption.. but it seems */
                                                         /* true  */
        looking += 3; 
      }
    looking++;
    now++;
    }
  temp[now]='\0';
  *desc_length=now;
  strcpy(string, temp);
}
      
void get_home(result)
char *result;
{
  char **cpp, home[MAXPATHLEN];
  struct passwd *pwd;
 
  if (strcpy(result,(char *)getenv("HOME")))
    return;
  if (pwd = getpwuid(getuid())){
    strcpy(result, pwd->pw_dir);
    return;
  }
#ifdef HESIOD
  if (cpp=(char **)hes_resolve(getlogin(),"filsys")){
    sscanf(*cpp,"AFS %s w %s",result,home);
    return;
  }
#endif
  else{
    strcpy(result, "/");
    return;
  }
}

void switch_disp(w, client_data, cbs)
Widget w;
XtPointer client_data;
XmPushButtonCallbackStruct *cbs;
{
  XmString name;  /*  either Hex or ASCII  */
  int foo, x;
  XmString tempstring;


  XtVaGetValues(browser_switch, XmNlabelString, &name, NULL);
  
  if (XmStringCompare(name, ascii_string)){
    XmTextSetString(browser_text, ascii);
    XtVaSetValues(browser_switch, XmNlabelString, hex_string, NULL);
  }
  else{
    XmTextSetString(browser_text, hex);
    XtVaSetValues(browser_switch, XmNlabelString, ascii_string, NULL);
  }
  XmStringFree(name);
  return;
}
    
void exit_well (w, client_data, cbs)
Widget w;
XtPointer client_data;
XmPushButtonCallbackStruct *cbs;
{ 
  exit(0);
}
