/*
 * kernsyms.c
 *
 * Get all kernel symbols defined in ppc.library
 *
 * Written by Frank Wille <frank@phoenix.owl.de> in 1998.
 *
 *
 * V0.1  22-Nov-98
 *       Created. Should work with ppc.library 46.19.
 *       For older libraries, search method 1 (-m 1) could be successful.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define VERSION 0
#define REVISION 1
#define IDSTRING "_KERNEL_FunctionTable"
#define BUFSIZE 256
#define MAXMETH 1



main(int argc,char *argv[])
{
  char *id = IDSTRING;
  char *srcname = NULL;
  char *ppcname = NULL;
  int i;
  FILE *fh,*src;
  int searchmethod = 0;
  int idcnt = 2;  /* sync on idcnt. id-string for symbol table */
  int minstrcnt = 50;  /* minimum of strings starting with PPC... */

  if (argc < 2) {
    printf("kernsyms v%d.%d\nUsage:\n  %s [-o ppcsource.s] "
           "[-m <searchmethod>] <ppc.library>\n",
           VERSION,REVISION,argv[0]);
    exit(0);
  }

  /* parse arguments */
  for (i=1; i<argc; i++) {
    if (argv[i][0] == '-') {
      switch (argv[i][1]) {
        case 'o':
          if (i < argc-1) {
            srcname = argv[++i];
          }
          else {
            fprintf(stderr,"%s: -o requires file name\n",argv[0]);
            exit(1);
          }
          break;
        case 'm':
          if (i < argc-1) {
            searchmethod = atoi(argv[++i]);
            if (searchmethod<0 || searchmethod>MAXMETH) {
              fprintf(stderr,"%s: -m: valid mathod ids: 0-%d\n",
                      argv[0],MAXMETH);
              exit(1);
            }
          }
          else {
            fprintf(stderr,"%s: -m requires method id (0-%d)\n",
                    argv[0],MAXMETH);
            exit(1);
          }
          break;
        default:
          fprintf(stderr,"%s: Unknown option %s\n",argv[0],argv[i]);
          exit(1);
      }
    }
    else
      ppcname = argv[i];
  }

  if (ppcname) {
    if (fh = fopen(ppcname,"r")) {
      char namebuf[BUFSIZE];
      char *libbuf,*p;
      int idlen = strlen(id)+1;
      int found = 0;
      long size;

      /* determine file size */
      fseek(fh,0,SEEK_END);
      size = ftell(fh);
      fseek(fh,0,SEEK_SET);
      if (size < 0) {
        fclose(fh);
        fprintf(stderr,"%s: seek error on %s\n",argv[0],ppcname);
        exit(1);
      }

      /* allocate buffer and read ppc.library */
      if (!(libbuf = malloc(size))) {
        fclose(fh);
        fprintf(stderr,"%s: not enough memory\n",argv[0]);
        exit(1);
      }
      if (fread(libbuf,1,size,fh) != size) {
        fclose(fh);
        fprintf(stderr,"%s: %s had a read error\n",argv[0],ppcname);
        exit(1);
      }
      fclose(fh);

      p = libbuf;
      switch (searchmethod) {
        case 0:
          /* method 0: IDSTRING search */
          while ((p-libbuf) < (size-idlen)) {
            if (!strcmp(p,id) && --idcnt==0) {
              found++;
              break;
            }
            p++;
          }
          if (found)
            p += idlen;
          break;

        case 1:
          /* method 1: search for names starting with PPC,  */
          /* a minimum of minstrcnt strings must be present */
          while ((p-libbuf) < size) {
            if (!strncmp(p,"PPC",3)) {
              char *oldp = p;  /* possible beginning of symbol table */
              int strcnt = 0;

              while ((p-libbuf) < size) {
                while (isgraph(*p) && (p-libbuf)<size)
                  p++;
                if (*p++ != '\0')  /* name was correctly terminated by \0? */
                  break;
                strcnt++;
                if (*p == '\0')  /* another \0? */
                  p++;
                if (!isgraph(*p))
                  break;
              }
              p = oldp;
              if (strcnt >= minstrcnt) {
                found++;
                break;
              }
            }
            p++;
          }
          break;
      }

      if (found) {
        /* open source file for writing if required */
        if (srcname) {
          if (!(fh = fopen(srcname,"w"))) {
            fprintf(stderr,"%s: Can't open %s for writing\n",argv[0],srcname);
            exit(1);
          }
        }
        else
          fh = NULL;

        /* get function names */
        while (isgraph(*p) && (p-libbuf)<size) {
          for (i=0; i<(BUFSIZE-1); i++,p++) {
            if (isgraph(*p))
              namebuf[i] = *p;
            else
              break;
          }
          namebuf[i] = 0;
          if (*p++ == '\0') {  /* name was correctly terminated by \0 ? */
            printf("%s\n",namebuf);
            if (fh)
              fprintf(fh,"\t.globl\t%s\n%s:\n\t.type\t%s,@function\n\n",
                      namebuf,namebuf,namebuf);
            if (*p=='\0' && (p-libbuf)<size) /*another 0? Ok,but only one!*/
              p++;
          }
          else
            break;  /* crap follows - that's it */
        }
        if (fh)
          fclose(fh);
      }
      else {
        fprintf(stderr,"%s: Identifier not found in %s\n",argv[0],ppcname);
        exit(1);
      }
    }
    else {
      fprintf(stderr,"%s: Can't open %s\n",argv[0],ppcname);
      exit(1);
    }
  }

  else {
    fprintf(stderr,"%s: no ppc.library specified\n",argv[0]);
    exit(1);
  }

  exit(0);
}
