본문으로 바로가기

C로 만드는 base64 디코드 커맨드

category Tcl & Tk/팁 (Tip) 2025. 3. 26. 16:34

출처: http://www.cs.man.ac.uk/~fellowsd/tcl/base64decode.c

 

C로 작성된 base64 확장 커맨드입니다. C로 확장하는 방법을 배우는데도 꽤 괜찮은 예제라 생각합니다.

#ifdef TCLCMD
#include <tcl.h>
#else
#include <stdio.h>
#endif
#include <string.h>
static const char encoding_vector[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

#ifdef TCLCMD

static int atend;

static int
getidx(char *buffer, int len, int *posn) {
  char c;
  char *idx;
  if (atend) return -1;
  do {
    if ((*posn)>=len) {
      atend = 1;
      return -1;
    }
    c = buffer[*posn++];
    if (c<0 || c=='=') {
      atend = 1;
      return -1;
    }
    idx = strchr(encoding_vector, c);
  } while (!idx);
  return idx - encoding_vector;
}

static int
base64decode(ClientData clientData, Tcl_Interp *interp,
             int objc, Tcl_Obj **objv) {
  Tcl_Obj *result;
  char *inbuffer;
  int ilen, olen, pos;
  char outbuffer[3];
  int c[4];

  if (objc!=2) {
    Tcl_SetObjResult(interp, Tcl_NewStringObj("needs one argument "
                                              "(string to decode)", -1));
    return TCL_ERROR;
  }
  inbuffer = Tcl_GetStringFromObj(interp, objv[1], &ilen);
  result = Tcl_NewStringObj("", 0);
  pos = 0;
  atend = 0;
  while (!atend) {
    c[0] = getidx(inbuffer, ilen, &pos);
    c[1] = getidx(inbuffer, ilen, &pos);
    c[2] = getidx(inbuffer, ilen, &pos);
    c[3] = getidx(inbuffer, ilen, &pos);

    olen = 0;
    if (c[0]>=0 && c[1]>=0) {
      outbuffer[0] = ((c[0]<<2)&0xfc)|((c[1]>>4)&0x03);
      olen++;
      if (c[2]>=0) {
        outbuffer[1] = ((c[1]<<4)&0xf0)|((c[2]>>2)&0x0f);
        olen++;
        if (c[3]>=0) {
          outbuffer[2] = ((c[2]<<6)&0xc0)|((c[3])&0x3f);
          olen++;
        }
      }
    }

    Tcl_StringObjAppend(interp, result, outbuffer, olen);
  }
  Tcl_SetObjResult(interp, result);
  return TCL_OK;
}

int
Base64decode_Init(Tcl_Interp *interp) {
  if (TCL_VERSION<8) {
    Tcl_AppendResult(interp, "Need at least Tcl8 for binary string support",
                     NULL);
    return TCL_ERROR;
  }
  Tcl_CreateObjCommand(interp, "base64decode", -1, base64decode,
                       (ClientData)0, NULL);
  return TCL_OK;
}

#else
static int atend;

static int
getidx(void) {
  char c;
  char *idx;
  if (atend) return -1;
  do {
    c = getchar();
    if (c<0 || c=='=') {
      atend = 1;
      return -1;
    }
    idx = strchr(encoding_vector, c);
  } while (!idx);
  return idx - encoding_vector;
}

int
main(int argc, char **argv) {
  int char1, char2, char3, char4;
  atend = 0;
  while (!atend) {
    char1 = getidx();
    char2 = getidx();
    char3 = getidx();
    char4 = getidx();
    if (char1>=0 && char2>=0) {
      putchar(((char1<<2)&0xfc)|((char2>>4)&0x03));
      if (char3>=0) {
        putchar(((char2<<4)&0xf0)|((char3>>2)&0x0f));
        if (char4>=0) {
          putchar(((char3<<6)&0xc0)|((char4)&0x3f));
        }
      }
    }
  }
  return 0;
}
#endif