Back to index

plt-scheme  4.2.1
ustart.c
Go to the documentation of this file.
00001 
00002 /* "Embedding" program for Unix/X11, to be used as
00003    an alternative to embedding in the actual MzScheme
00004    or MrEd binary. */
00005 
00006 #include <sys/types.h>
00007 #include <sys/uio.h>
00008 #include <stdlib.h>
00009 #include <unistd.h>
00010 #include <string.h>
00011 #include <fcntl.h>
00012 #include <errno.h>
00013 
00014 /* The config string after : is replaced with ! followed by a sequence
00015    of little-endian 4-byte ints:
00016     start - offset into the binary
00017     prog_end - offset; start to prog_end is the program region
00018     end - offset; prog_end to end is the command region
00019     count - number of cmdline args in command region
00020     x11? - non-zero => launches MrEd for X
00021 
00022     In the command region, the format is a sequence of NUL-terminated strings:
00023      exe_path - program to start (relative is w.r.t. executable)
00024      dll_path - DLL directory if non-empty (relative is w.r.t. executable)
00025      cmdline_arg ...
00026 */
00027 char *config = "cOnFiG:[***************************";
00028 
00029 char *binary_type_hack = "bINARy tYPe:ezic";
00030 
00031 /* This path list is used instead of the one in the MzScheme/MrEd
00032    binary. That way, the same MzScheme/MrEd binary can be shared
00033    among embedding exectuables that have different collection
00034    paths. */
00035 char *_coldir = "coLLECTs dIRECTORy:" /* <- this tag stays, so we can find it again */
00036                 "../collects"
00037                 "\0\0" /* <- 1st nul terminates path, 2nd terminates path list */
00038                 /* Pad with at least 1024 bytes: */
00039                 "****************************************************************"
00040               "****************************************************************"
00041               "****************************************************************"
00042               "****************************************************************"
00043               "****************************************************************"
00044               "****************************************************************"
00045               "****************************************************************"
00046               "****************************************************************"
00047               "****************************************************************"
00048               "****************************************************************"
00049               "****************************************************************"
00050               "****************************************************************"
00051               "****************************************************************"
00052               "****************************************************************"
00053               "****************************************************************"
00054               "****************************************************************";
00055 static int _coldir_offset = 19; /* Skip permanent tag */
00056 
00057 typedef struct {
00058   char *flag;
00059   int arg_count;
00060 } X_flag_entry;
00061 
00062 static X_flag_entry X_flags[] = {
00063   { "-display", 1 },
00064   { "-geometry", 1 },
00065   { "-bg", 1 },
00066   { "-background", 1 },
00067   { "-fg", 1 },
00068   { "-foreground", 1 },
00069   { "-fn", 1 },
00070   { "-font", 1 },
00071   { "-iconic", 0 },
00072   { "-name", 1 },
00073   { "-rv", 0 },
00074   { "-reverse", 0 },
00075   { "+rv", 0 },
00076   { "-selectionTimeout", 1 },
00077   { "-synchronous", 0 },
00078   { "-title", 1 },
00079   { "-xnllanguage", 1 },
00080   { "-xrm", 1 },
00081   { "-singleInstance", 0 },
00082   { NULL, 0 }
00083 };
00084 
00085 static int is_x_flag(char *s)
00086 {
00087   X_flag_entry *x = X_flags;
00088 
00089   while (x->flag) {
00090     if (!strcmp(x->flag, s))
00091       return x->arg_count + 1;
00092     x++;
00093   }
00094 
00095   return 0;
00096 }
00097 
00098 static void write_str(int fd, char *s)
00099 {
00100   write(fd, s, strlen(s));
00101 }
00102 
00103 #if 0
00104 /* Useful for debugging: */
00105 static char *num_to_string(int n)
00106 {
00107   if (!n)
00108     return "0";
00109   else {
00110     char *d = (char *)malloc(20) + 19;
00111     *d = 0;
00112     while (n) {
00113       d--;
00114       *d = (n % 10) + '0';
00115       n = n / 10;
00116     }
00117     return d;
00118   }
00119 }
00120 #endif
00121 
00122 static char *string_append(char *s1, char *s2)
00123 {
00124   int l1, l2;
00125   char *s;
00126 
00127   l1 = strlen(s1);
00128   l2 = strlen(s2);
00129 
00130   s  = (char *)malloc(l1 + l2 + 1);
00131 
00132   memcpy(s, s1, l1);
00133   memcpy(s + l1, s2, l2);
00134   s[l1 + l2] = 0;
00135 
00136   return s;
00137 }
00138 
00139 static char *copy_string(char *s1)
00140 {
00141   int l1;
00142   char *s;
00143 
00144   if (!s1) return NULL;
00145 
00146   l1 = strlen(s1);
00147 
00148   s  = (char *)malloc(l1 + 1);
00149 
00150   memcpy(s, s1, l1 + 1);
00151 
00152   return s;
00153 }
00154 
00155 static char *do_path_append(char *s1, int l1, char *s2)
00156 {
00157   int l2;
00158   char *s;
00159 
00160   l2 = strlen(s2);
00161 
00162   s  = (char *)malloc(l1 + l2 + 2);
00163 
00164   memcpy(s, s1, l1);
00165   if (s[l1 - 1] != '/') {
00166     s[l1++] = '/';
00167   }
00168 
00169   memcpy(s + l1, s2, l2);
00170   s[l1 + l2] = 0;
00171 
00172   return s;
00173 }
00174 
00175 static char *path_append(char *s1, char *s2)
00176 {
00177   return do_path_append(s1, strlen(s1), s2);
00178 }
00179 
00180 static int executable_exists(char *path)
00181 {
00182   return (access(path, X_OK) == 0);
00183 }
00184 
00185 static int as_int(char *_c)
00186 {
00187   unsigned char *c = (unsigned char *)_c;
00188   return c[0] | ((int)c[1] << 8) | ((int)c[2] << 16)  | ((int)c[3] << 24);
00189 }
00190 
00191 static int has_slash(char *s)
00192 {
00193   while (*s) {
00194     if (s[0] == '/')
00195       return 1;
00196     s++;
00197   }
00198   return 0;
00199 }
00200 
00201 char *absolutize(char *p, char *d)
00202 {
00203   int l1;
00204 
00205   if (!p[0])
00206     return p;
00207 
00208   if (p[0] == '/')
00209     return p;
00210   
00211   /* Strip filename off d: */
00212   l1 = strlen(d);
00213   while (l1 && (d[l1- 1] != '/')) {
00214     l1--;
00215   }
00216   if (l1)
00217     return do_path_append(d, l1, p);
00218   else
00219     return p;
00220 }
00221 
00222 static char *next_string(char *s)
00223 {
00224   return s + strlen(s) + 1;
00225 }
00226 
00227 int main(int argc, char **argv)
00228 {
00229   char *me = argv[0], *data, **new_argv;
00230   char *exe_path, *lib_path, *dll_path;
00231   int start, prog_end, end, count, fd, v, x11;
00232   int argpos, inpos, collcount = 1;
00233 
00234   if (config[7] == '[') {
00235     write_str(2, argv[0]);
00236     write_str(2, ": this is an unconfigured starter\n");
00237     return 1;
00238   }
00239 
00240   if (me[0] == '/') {
00241     /* Absolute path */
00242   } else if (has_slash(me)) {
00243     /* Relative path with a directory: */
00244     char *buf;
00245     long buflen = 4096;
00246     buf = (char *)malloc(buflen);
00247     me = path_append(getcwd(buf, buflen), me);
00248   } else {
00249     /* We have to find the executable by searching PATH: */
00250     char *path = copy_string(getenv("PATH")), *p, *m;
00251     int more;
00252 
00253     if (!path) {
00254       path = "";
00255     }
00256 
00257     while (1) {
00258       /* Try each element of path: */
00259       for (p = path; *p && (*p != ':'); p++) { }
00260       if (*p) {
00261        *p = 0;
00262        more = 1;
00263       } else
00264        more = 0;
00265 
00266       if (!*path)
00267        break;
00268 
00269       m = path_append(path, me);
00270 
00271       if (executable_exists(m)) {
00272        if (m[0] != '/')
00273          m = path_append(getcwd(NULL, 0), m);
00274        me = m;
00275        break;
00276       }
00277       free(m);
00278 
00279       if (more)
00280        path = p + 1;
00281       else
00282        break;
00283     }
00284   }
00285   
00286   /* me is now an absolute path to the binary */
00287 
00288   /* resolve soft links */
00289   while (1) {
00290     int len, bufsize = 127;
00291     char *buf;
00292     buf = (char *)malloc(bufsize + 1);
00293     len = readlink(me, buf, bufsize);
00294     if (len < 0) {
00295       if (errno == ENAMETOOLONG) {
00296        /* Increase buffer size and try again: */
00297        bufsize *= 2;
00298        buf = (char *)malloc(bufsize + 1);
00299       } else
00300        break;
00301     } else {
00302       /* Resolve buf relative to me: */
00303       buf[len] = 0;
00304       buf = absolutize(buf, me);
00305       me = buf;
00306       buf = (char *)malloc(bufsize + 1);
00307     }
00308   }
00309 
00310   start = as_int(config + 8);
00311   prog_end = as_int(config + 12);
00312   end = as_int(config + 16);
00313   count = as_int(config + 20);
00314   x11 = as_int(config + 24);
00315 
00316   {
00317     int offset, len;
00318     offset = _coldir_offset;
00319     while (1) {
00320       len = strlen(_coldir + offset);
00321       offset += len + 1;
00322       if (!_coldir[offset])
00323        break;
00324       collcount++;
00325     }
00326   }
00327 
00328   data = (char *)malloc(end - prog_end);
00329   new_argv = (char **)malloc((count + argc + (2 * collcount) + 8) * sizeof(char*));
00330 
00331   fd = open(me, O_RDONLY, 0);
00332   lseek(fd, prog_end, SEEK_SET);
00333   read(fd, data, end - prog_end);
00334   close(fd);
00335   
00336   exe_path = data;
00337   data = next_string(data);
00338 
00339   lib_path = data;
00340   data = next_string(data);
00341 
00342   exe_path = absolutize(exe_path, me);
00343   lib_path = absolutize(lib_path, me);
00344 
00345 # ifdef OS_X
00346 #  define LD_LIB_PATH "DYLD_LIBRARY_PATH"
00347 # else
00348 #  define LD_LIB_PATH "LD_LIBRARY_PATH"
00349 # endif
00350 
00351   if (*lib_path) {
00352     dll_path = getenv(LD_LIB_PATH);
00353     if (!dll_path) {
00354       dll_path = "";
00355     }
00356     dll_path = string_append(dll_path, ":");
00357     dll_path = string_append(lib_path, dll_path);
00358     dll_path = string_append(LD_LIB_PATH "=", dll_path);
00359     putenv(dll_path);
00360   }
00361 
00362   new_argv[0] = me;
00363 
00364   argpos = 1;
00365   inpos = 1;
00366 
00367   /* Keep all X11 flags to the front: */
00368   if (x11) {
00369     int n;
00370     while (inpos < argc) {
00371       n = is_x_flag(argv[inpos]);
00372       if (!n)
00373        break;
00374       if (inpos + n > argc) {
00375        write_str(2, argv[0]);
00376        write_str(2, ": missing an argument for ");
00377        write_str(2, argv[inpos]);
00378        write_str(2, "\n");
00379        return 1;
00380       }
00381       while (n--) {
00382        new_argv[argpos++] = argv[inpos++];
00383       }
00384     }
00385   }
00386 
00387   /* Add -X and -S flags */
00388   {
00389     int offset, len;
00390     offset = _coldir_offset;
00391     new_argv[argpos++] = "-X";
00392     new_argv[argpos++] = _coldir + offset;
00393     while (1) {
00394       len = strlen(_coldir + offset);
00395       offset += len + 1;
00396       if (!_coldir[offset])
00397        break;
00398       new_argv[argpos++] = "-S";
00399       new_argv[argpos++] = _coldir + offset;
00400     }
00401   }
00402 
00403   /* Add built-in flags: */
00404   while (count--) {
00405     new_argv[argpos++] = data;
00406     data = next_string(data);
00407   }
00408 
00409   /* Propagate new flags (after the X11 flags) */
00410   while (inpos < argc) {
00411     new_argv[argpos++] = argv[inpos++];
00412   }
00413 
00414   new_argv[argpos] = NULL;
00415 
00416   /* Execute the original binary: */
00417 
00418   v = execv(exe_path, new_argv);
00419 
00420   write_str(2, argv[0]);
00421   write_str(2, ": failed to start ");
00422   write_str(2, exe_path);
00423   write_str(2, "\n");
00424 
00425   return v;
00426 }