master xplshn/aruu / cmd / posix / uuencode.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/stat.h>
  4
  5#include <stdio.h>
  6#include <string.h>
  7
  8#include "util.h"
  9
 10static unsigned int
 11b64e(unsigned char *b)
 12{
 13  unsigned int o, p = b[2] | (b[1] << 8) | (b[0] << 16);
 14  const char   b64et[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw"
 15                         "xyz0123456789+/";
 16
 17  o = b64et[p & 0x3f];
 18  p >>= 6;
 19  o = (o << 8) | b64et[p & 0x3f];
 20  p >>= 6;
 21  o = (o << 8) | b64et[p & 0x3f];
 22  p >>= 6;
 23  o = (o << 8) | b64et[p & 0x3f];
 24
 25  return o;
 26}
 27
 28static void
 29uuencodeb64(FILE *fp, const char *name, const char *s)
 30{
 31  struct stat   st;
 32  ssize_t       n, m = 0;
 33  unsigned char buf[45], *pb;
 34  unsigned int  out[sizeof(buf) / 3 + 1], *po;
 35
 36  if (fstat(fileno(fp), &st) < 0)
 37    eprintf("fstat %s:", s);
 38  printf("begin-base64 %o %s\n", st.st_mode & 0777, name);
 39  /* write line by line */
 40  while ((n = fread(buf, 1, sizeof(buf), fp))) {
 41    /* clear old buffer if converting with non-multiple of 3 */
 42    if (n != sizeof(buf) && (m = n % 3) != 0) {
 43      buf[n] = '\0'; /* m == 2 */
 44      if (m == 1)
 45        buf[n + 1] = '\0'; /* m == 1 */
 46    }
 47    for (pb = buf, po = out; pb < buf + n; pb += 3)
 48      *po++ = b64e(pb);
 49    if (m != 0) {
 50      unsigned int mask = 0xffffffff, dest = 0x3d3d3d3d;
 51      /* m==2 -> 0x00ffffff; m==1 -> 0x0000ffff */
 52      mask >>= ((3 - m) << 3);
 53      po[-1] = (po[-1] & mask) | (dest & ~mask);
 54    }
 55    *po++ = '\n';
 56    fwrite(out, 1, (po - out) * sizeof(unsigned int) - 3, stdout);
 57  }
 58  if (ferror(fp))
 59    eprintf("'%s' read error:", s);
 60  puts("====");
 61}
 62
 63static void
 64uuencode(FILE *fp, const char *name, const char *s)
 65{
 66  struct stat   st;
 67  unsigned char buf[45], *p;
 68  ssize_t       n;
 69  int           ch;
 70
 71  if (fstat(fileno(fp), &st) < 0)
 72    eprintf("fstat %s:", s);
 73  printf("begin %o %s\n", st.st_mode & 0777, name);
 74  while ((n = fread(buf, 1, sizeof(buf), fp))) {
 75    ch = ' ' + (n & 0x3f);
 76    putchar(ch == ' ' ? '`' : ch);
 77    for (p = buf; n > 0; n -= 3, p += 3) {
 78      if (n < 3) {
 79        p[2] = '\0';
 80        if (n < 2)
 81          p[1] = '\0';
 82      }
 83      ch = ' ' + ((p[0] >> 2) & 0x3f);
 84      putchar(ch == ' ' ? '`' : ch);
 85      ch = ' ' + (((p[0] << 4) | ((p[1] >> 4) & 0xf)) & 0x3f);
 86      putchar(ch == ' ' ? '`' : ch);
 87      ch = ' ' + (((p[1] << 2) | ((p[2] >> 6) & 0x3)) & 0x3f);
 88      putchar(ch == ' ' ? '`' : ch);
 89      ch = ' ' + (p[2] & 0x3f);
 90      putchar(ch == ' ' ? '`' : ch);
 91    }
 92    putchar('\n');
 93  }
 94  if (ferror(fp))
 95    eprintf("'%s' read error:", s);
 96  printf("%c\nend\n", '`');
 97}
 98
 99static void
100usage(void)
101{
102  eprintf("usage: %s [-m] [file] name\n", argv0);
103}
104
105// ?man uuencode: encode binary file
106// ?man arguments: [file] name
107// ?man encode a binary file into ascii text
108int
109main(int argc, char *argv[])
110{
111  FILE *fp                                               = NULL;
112  void (*uuencode_f)(FILE *, const char *, const char *) = uuencode;
113  int ret                                                = 0;
114
115  ARGBEGIN
116  {
117    // ?man -m: specify mode or limit
118    case 'm':
119      uuencode_f = uuencodeb64;
120      break;
121    default:
122      usage();
123  }
124  ARGEND
125
126  if (!argc || argc > 2)
127    usage();
128
129  if (argc == 1 || !strcmp(argv[0], "-")) {
130    uuencode_f(stdin, argv[0], "<stdin>");
131  } else {
132    if (!(fp = fopen(argv[0], "r")))
133      eprintf("fopen %s:", argv[0]);
134    uuencode_f(fp, argv[1], argv[0]);
135  }
136
137  ret |= fp && fshut(fp, argv[0]);
138  ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
139
140  return ret;
141}