示例#1
0
文件: mob.c 项目: gerdr/m0
static bool verify_header(struct loader *loader)
{
	const m0_mobheader *header = (m0_mobheader *)read(loader, sizeof *header);
	if(!header) return 0;

	if(memcmp(header->magic, HEADER.magic, sizeof HEADER.magic))
	{
		cry(loader, "file <%s> has wrong magic number", loader->name);
		return 0;
	}

	if(memcmp(header->version, HEADER.version, sizeof HEADER.version))
	{
		cry(loader, "file <%s> has wrong version", loader->name);
		return 0;
	}

	if(memcmp(header->config, HEADER.config, sizeof HEADER.config))
	{
		cry(loader, "file <%s> has wrong type configuration", loader->name);
		return 0;
	}

	if(header->size != loader->size)
	{
		cry(loader, "file <%s> has incorrect size", loader->name);
		return 0;
	}

	return 1;
}
示例#2
0
static int load_dll(struct mg_context *ctx, const char *dll_name,
                    struct ssl_func *sw) {
  union {void *p; void (*fp)(void);} u;
  void  *dll_handle;
  struct ssl_func *fp;

  if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
    cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
    return 0;
  }

  for (fp = sw; fp->name != NULL; fp++) {
#ifdef _WIN32
    // GetProcAddress() returns pointer to function
    u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
#else
    // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
    // function pointers. We need to use a union to make a cast.
    u.p = dlsym(dll_handle, fp->name);
#endif // _WIN32
    if (u.fp == NULL) {
      cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
      return 0;
    } else {
      fp->ptr = u.fp;
    }
  }

  return 1;
}
示例#3
0
文件: mob.c 项目: gerdr/m0
static bool load_chunks(struct loader *loader)
{
	const m0_segment *dir = (m0_segment *)read(loader, sizeof *dir);
	if(!dir) return 0;

	// TODO: verify id - need to come up with a general scheme

	if(!m0_interp_reserve_chunks(loader->interp, dir->entry_count) ||
		!m0_interp_reserve_chunk_map_slots(loader->interp, dir->entry_count))
	{
		cry(loader, "failed to reserve %u chunks for file <%s>",
			(unsigned)dir->entry_count, loader->name);

		return 0;
	}

	for(size_t i = 0; i < dir->entry_count; ++i)
	{
		const m0_direntry *entry = (m0_direntry *)read(loader, sizeof *entry);
		if(!entry) return 0;
			// no need to rollback reserved chunks:
			// the worst that can happen are superfluous realloc() calls

		uint32_t *blocks = (uint32_t *)loader->mapping;

		if(entry->const_offset % sizeof *blocks ||
			entry->meta_offset % sizeof *blocks ||
			entry->code_offset % sizeof *blocks)
		{
			cry(loader, "illegal offsets in file <%s>", loader->name);
			return 0;
		}

		const m0_chunk chunk = {
			(m0_string *)loader->cursor,
			(m0_segment *)(blocks + entry->const_offset / sizeof *blocks),
			(m0_segment *)(blocks + entry->meta_offset / sizeof *blocks),
			(m0_segment *)(blocks + entry->code_offset / sizeof *blocks)
		};

		size_t chunk_id = m0_interp_push_reserved_chunk(
			loader->interp, &chunk);

		if(!m0_interp_register_reserved_chunk(
			loader->interp, chunk.name, chunk_id))
		{
			cry(loader, "allocation failure while loading file <%s>",
				loader->name);

			return 0;
		}

		// skip name
		read(loader, entry->byte_size - sizeof *entry);
	}

	return 1;
}
示例#4
0
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
static int set_ssl_option(struct mg_context *ctx) {
  int i, size;
  const char *pem;

  // If PEM file is not specified and the init_ssl callback
  // is not specified, skip SSL initialization.
  if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
    //  MG_INIT_SSL
    //  ctx->callbacks.init_ssl == NULL) {
    return 1;
  }

#if !defined(NO_SSL_DL)
  if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
      !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
    return 0;
  }
#endif // NO_SSL_DL

  // Initialize SSL library
  SSL_library_init();
  SSL_load_error_strings();

  if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
    cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error());
    return 0;
  }

  // If user callback returned non-NULL, that means that user callback has
  // set up certificate itself. In this case, skip sertificate setting.
  // MG_INIT_SSL
  if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
      SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) {
    cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
    return 0;
  }

  if (pem != NULL) {
    (void) SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem);
  }

  // Initialize locking callbacks, needed for thread safety.
  // http://www.openssl.org/support/faq.html#PROG1
  size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
  if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
    cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
    return 0;
  }

  for (i = 0; i < CRYPTO_num_locks(); i++) {
    pthread_mutex_init(&ssl_mutexes[i], NULL);
  }

  CRYPTO_set_locking_callback(&ssl_locking_callback);
  CRYPTO_set_id_callback(&ssl_id_callback);

  return 1;
}
示例#5
0
REPLACE_STATIC pid_t spawn_process(struct mg_connection *conn, const char *prog,
                           char *envblk, char *envp[], int fd_stdin,
                           int fd_stdout, const char *dir) {
  pid_t pid;
  const char *interp;

  envblk = NULL; // Unused

  if ((pid = fork()) == -1) {
    // Parent
    send_http_error(conn, 500, http_500_error, "fork(): %s", strerror(ERRNO));
  } else if (pid == 0) {
    // Child
    if (chdir(dir) != 0) {
      cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
    } else if (dup2(fd_stdin, 0) == -1) {
      cry(conn, "%s: dup2(%d, 0): %s", __func__, fd_stdin, strerror(ERRNO));
    } else if (dup2(fd_stdout, 1) == -1) {
      cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO));
    } else {
      (void) dup2(fd_stdout, 2);
      (void) close(fd_stdin);
      (void) close(fd_stdout);

      // After exec, all signal handlers are restored to their default values,
      // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
      // implementation, SIGCHLD's handler will leave unchanged after exec
      // if it was set to be ignored. Restore it to default action.
      signal(SIGCHLD, SIG_DFL);

      interp = conn->ctx->config[CGI_INTERPRETER];
      if (interp == NULL) {
        (void) execle(prog, prog, NULL, envp);
        cry(conn, "%s: execle(%s): %s", __func__, prog, strerror(ERRNO));
      } else {
        (void) execle(interp, interp, prog, NULL, envp);
        cry(conn, "%s: execle(%s %s): %s", __func__, interp, prog,
            strerror(ERRNO));
      }
    }
    exit(EXIT_FAILURE);
  }

  // Parent. Close stdio descriptors
  (void) close(fd_stdin);
  (void) close(fd_stdout);

  return pid;
}
示例#6
0
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, int port) {
  struct mg_context *ctx;

  // Allocate context and initialize reasonable general case defaults.
  // TODO(lsm): do proper error handling here.
  ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
  ctx->user_callback = user_callback;
  ctx->user_data = user_data;

  if (!set_ports_option(ctx, port)) {
    free_context(ctx);
    return NULL;
  }
  // Ignore SIGPIPE signal, so if browser cancels the request, it
  // won't kill the whole process.
  (void) signal(SIGPIPE, SIG_IGN);
  (void) pthread_mutex_init(&ctx->mutex, NULL);
  (void) pthread_cond_init(&ctx->cond, NULL);
  (void) pthread_cond_init(&ctx->sq_empty, NULL);
  (void) pthread_cond_init(&ctx->sq_full, NULL);

  // Start master (listening) thread
  start_thread(ctx, (mg_thread_func_t) master_thread, ctx);

  // Start worker threads
  for (int i = 0; i < NUM_THREADS; i++) {
    if (start_thread(ctx, (mg_thread_func_t) worker_thread, ctx) != 0) {
      cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
    } else {
      ctx->num_threads++;
    }
  }

  return ctx;
}
示例#7
0
/*
 * Send error message back to a client.
 */
int send_error(struct mg_connection *conn, int status, const char *reason, const char *fmt, ...) {
  va_list	ap;

  conn->status_code = status;

  (void) mg_printf(conn,
		   "HTTP/1.1 %d %s\r\n"
		   "Content-Type: text/html\r\n"
		   "Connection: close\r\n"
		   "\r\n", status, reason);

  /* Errors 1xx, 204 and 304 MUST NOT send a body */
  if(status > 199 && status != 204 && status != 304) {
    char buf[BUFSIZ];
    int  len;

    conn->num_bytes_sent = 0;
    va_start(ap, fmt);
    len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);
    va_end(ap);
    conn->num_bytes_sent += mg_write(conn, buf, len);
    cry(conn, "%s", buf);
  }

  return(1);
}
示例#8
0
void mg_send_http_error(struct mg_connection *conn, int status,
                        const char *reason, const char *fmt, ...) {
  char buf[BUFSIZ];
  va_list ap;
  int len;

  conn->request_info.status_code = status;

  buf[0] = '\0';
  len = 0;

  /* Errors 1xx, 204 and 304 MUST NOT send a body */
  if (status > 199 && status != 204 && status != 304) {
    len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
    cry(conn, "%s", buf);
    buf[len++] = '\n';

    va_start(ap, fmt);
    len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
    va_end(ap);
  }
  DEBUG_TRACE(("[%s]", buf));

  mg_printf(conn, "HTTP/1.1 %d %s\r\n"
      "Content-Type: text/plain\r\n"
      "Content-Length: %d\r\n"
      "Connection: %s\r\n\r\n", status, reason, len,
      suggest_connection_header(conn));
  conn->num_bytes_sent += mg_printf(conn, "%s", buf);
}
示例#9
0
// Like snprintf(), but never returns negative value, or the value
// that is larger than a supplied buffer.
// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
// in his audit report.
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
                        const char *fmt, va_list ap) {
  int n;

  if (buflen == 0)
    return 0;

  n = vsnprintf(buf, buflen, fmt, ap);

  if (n < 0) {
    cry(conn, "vsnprintf error");
    n = 0;
  } else if (n >= (int) buflen) {
    cry(conn, "truncating vsnprintf buffer: [%.*s]",
        n > 200 ? 200 : n, buf);
    n = (int) buflen - 1;
  }
  buf[n] = '\0';

  return n;
}
示例#10
0
文件: c_prog.c 项目: roskakori/sofa
c_prog (EIF_OBJ animals) {
  int i;
  EIF_OBJ eiffel_string;
  char* c_string;

  for (i=array_of_animal_lower(animals);
       i <= array_of_animal_upper(animals);
       i++) {
    eiffel_string = cry(array_of_animal_item(animals,i));
    c_string = string_to_external(eiffel_string);
    printf("%s\n",c_string);
  }
}
示例#11
0
文件: mob.c 项目: gerdr/m0
static inline void *read(struct loader *loader, size_t size)
{
	assert(size % sizeof *loader->cursor == 0);
	if(loader->remaining < size)
	{
		cry(loader, "premature end of file <%s>", loader->name);
		return NULL;
	}

	void *mark = loader->cursor;
	loader->remaining -= size;
	loader->cursor += size / sizeof *loader->cursor;
	return mark;
}
示例#12
0
文件: symtab.c 项目: hitmoon/ropc
void
make_symbol_used(const char *key)
{
    struct SYM *p = (struct SYM *)get_symbol(key); // wrong cast
    if (!p) {
        die("symbol '%s' not defined\n", key);
    }
    if (p->type != SYMBOL_EXTERN) {
        if (p->type != SYMBOL_NORMAL) {
            cry("using symbol '%s' is probably wrong\n", key);
        }
        p->used = USED;
    }
}
示例#13
0
文件: emit.c 项目: xerub/ropc
static void
maybe_symbol_forward(const char *arg)
{
    char *tmp = copy_address_sym(arg);
    if (tmp) {
        add_symbol_forward(tmp, 0);
        if (try_symbol_extern(tmp)) {
            die("symbol '%s' is actually an address\n", tmp);
        }
        if (try_symbol_attr(tmp) & ATTRIB_CONSTANT) {
            cry("taking address of constant variable '%s'\n", tmp);
        }
        free(tmp);
    }
}
示例#14
0
static int set_ports_option(struct mg_context *ctx, int port) {
  int reuseaddr = 1, success = 1;
  socklen_t sock_len = sizeof(ctx->local_address);
  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
  memset(&ctx->local_address, 0, sock_len);
  ctx->local_address.sin_family = AF_INET;
  ctx->local_address.sin_port = htons((uint16_t) port);
  ctx->local_address.sin_addr.s_addr = htonl(INADDR_ANY);

  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 500 * 1000;

  if ((ctx->local_socket = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||
      setsockopt(ctx->local_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) != 0 ||
      setsockopt(ctx->local_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0 ||
      bind(ctx->local_socket, (const struct sockaddr *) &ctx->local_address, sock_len) != 0 ||
      // TODO(steineldar): Replace 20 (max socket backlog len in connections).
      listen(ctx->local_socket, 20) != 0) {
    close(ctx->local_socket);
    cry(fc(ctx), "%s: cannot bind to port %d: %s", __func__,
        port, strerror(ERRNO));
    success = 0;
  } else if (getsockname(ctx->local_socket, (struct sockaddr *) &ctx->local_address, &sock_len)) {
    close(ctx->local_socket);
    cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
    success = 0;
  }

  if (!success) {
    ctx->local_socket = INVALID_SOCKET;
    close_all_listening_sockets(ctx);
  }

  return success;
}
示例#15
0
文件: emit.c 项目: xerub/ropc
static int
is_loadable(struct node *n, BOOL allow_deref)
{
    struct lval_node *p = (struct lval_node *)n;
    if (!is_loadable_sym(p->name)) {
        return 0;
    }
    if (p->deref && !allow_deref) {
        return 0;
    }
    if (!p->deref && try_symbol_extern(p->name)) {
        cry("symbol '%s' is actually an address\n", p->name);
        return -1;
    }
    return 1;
}
示例#16
0
文件: emit.c 项目: xerub/ropc
static BOOL
is_loadable_sym(const char *name)
{
    enum use_t u = get_symbol_used(name);
    if (u != UNUSED) {
        if (u == CLOBBERED) {
            // XXX gets reported twice
            cry("symbol '%s' should be volatile\n", name);
        }
        return FALSE;
    }
    if (try_symbol_attr(name) & ATTRIB_VOLATILE) {
        return FALSE;
    }
    return TRUE;
}
示例#17
0
static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
                        void *param) {
  pthread_t thread_id;
  pthread_attr_t attr;
  int retval;

  (void) pthread_attr_init(&attr);
  (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
  // TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
  // (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);

  if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) {
    cry(fc(ctx), "%s: %s", __func__, strerror(retval));
  }

  return retval;
}
示例#18
0
文件: mob.c 项目: gerdr/m0
static bool mmap_file(struct loader *loader)
{
	size_t size;
	void *mapping = m0_platform_mmap_file_private(loader->name, &size);
	if(!mapping)
	{
		cry(loader, "failed to mmap file <%s>", loader->name);
		return 0;
	}

	loader->mapping = mapping;
	loader->size = size;
	loader->cursor = (uint32_t *)loader->mapping;
	loader->remaining = size;

	return 1;
}
示例#19
0
文件: jre.cpp 项目: littlemole/moe
JNIEnv* JRE::createJVM(bool debug)
{
	int noptions = 7 + (debug ? 1 : 0);
	options_ = new JavaVMOption[noptions];

	//C:\\Program Files (x86)\\Java\\jre6\\lib\\rt.jar
	//std::string classpath = "-Djava.class.path=D:\\workspace\\dispdrivertest\\bin;" + mol::tostring(classpath_);
	//std::string libpath   = "-Djava.library.path=D:\\workspace\\dispdrivertest\\bin" +  mol::tostring(libpath_);

	mol::win::AppBase& app = mol::App();
	//JREApp& app = mol::app<JREApp>();
	std::wstring path =  app.getAppPath();
	path = mol::Path::pathname(path);
	path = path + _T("\\lib");

	std::string classpath = "-Djava.class.path=" + mol::tostring(path) + "\\classes;" + mol::tostring(path) + "\\dispdriver.jar";
	if ( !classpath_.empty() )
	{
		classpath += ";" + mol::tostring(classpath_);
	}
	std::string libpath   = "-Djava.library.path=" +  mol::tostring(path) ;
	if ( !libpath_.empty() )
	{
		libpath += ";" + mol::tostring(libpath_);
	}

	options_[0].optionString = (char*)classpath.c_str();
	options_[1].optionString = (char*)libpath.c_str();
	options_[2].optionString = "-Xdebug";
	options_[3].optionString = "-Xnoagent";
	options_[4].optionString = "-Djava.compiler=NONE";
	options_[5].optionString = "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005";
	options_[6].optionString = "-Dsun.java2d.d3d=false";

	if ( debug )
		options_[7].optionString = "-verbose:jni";

	vm_args_.version             = JNI_VERSION_1_6;
	vm_args_.nOptions            = noptions;
	vm_args_.options             = options_;
	vm_args_.ignoreUnrecognized  = false;

	if (!jruntime_)
		jruntime_ = loadJVM();
	if (!jruntime_)
	{
		cry();
		return 0;
	}
			
	CreateJavaVM createJavaVM = (CreateJavaVM) ::GetProcAddress( jruntime_, "JNI_CreateJavaVM" );

	if (!createJavaVM)
		return false;

	jint res = createJavaVM( &jvm_, (void**)&env_, &vm_args_ );
	if( res != 0 )
		return 0;

	jvm_->AttachCurrentThread((void**)&env_, &vm_args_);

	return env_;
}
示例#20
0
MemoryException::MemoryException (const char* file, int line) :  
  _file (file),                       
  _line (line)                        
{
  cry (cout);
} // MemoryException::MemoryException
示例#21
0
// mg.cry: Log an error. Default value for mg.onerror.
static int lsp_cry(lua_State *L){
  struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
  cry(conn, "%s", lua_tostring(L, -1));
  return 0;
}
示例#22
0
文件: emit.c 项目: xerub/ropc
void
emit_nodes(struct node *n, const char *assignto, BOOL force, BOOL inloop)
{
    char *fast = NULL;

    assert(n);

    if (n->next == NULL && n->type != NODE_CALL && !assignto && !force) {
        /* do not emit single node, unless it is a call or we really need it */
        cry("statement with no effect\n");
        return;
    }
    switch (n->type) {
        case NODE_IMM: {
            fast = AS_IMM(n)->value;
            maybe_symbol_forward(fast);
            if (force) {
                cry("constant expression '%s' in conditional\n", fast);
            }
            assert(!assignto);
            break;
        }
        case NODE_LVAL: {
            struct lval_node *p = AS_LVAL(n);
            int loadable = is_loadable(n, TRUE);
            if (loadable < 0) {
                fast = p->name;
            } else if (loadable > 0) {
                make_symbol_used(p->name);
                emit_load_direct(p->name, p->deref);
            } else {
                emit_load_indirect(p->name, p->deref);
            }
            break;
        }
        case NODE_CALL: {
            struct call_node *p = AS_CALL(n);
            struct node *parm;
            int deref0 = 0;
            char *args[MAX_FUNC_ARGS];
            char *func;
            int i;
            BOOL retval = (n->next != NULL) || force || assignto;
            BOOL direct = FALSE;
            for (i = 0, parm = p->parm; parm; parm = parm->next, i++) {
                BOOL r0 = (i == 0 && arch_regparm);
                assert(i < MAX_FUNC_ARGS);
                if (parm->type == NODE_IMM) {
                    args[i] = xstrdup(AS_IMM(parm)->value);
                    maybe_symbol_forward(args[i]);
                } else if (parm->type == NODE_LVAL && is_loadable(parm, r0)) {
                    struct lval_node *q = AS_LVAL(parm);
                    args[i] = xstrdup(q->name);
                    make_symbol_used(args[i]);
                    if (q->deref) {
                        deref0 = 1;
                    }
                } else if (r0 && parm->next == NULL) {
                    args[i] = NULL;
                    direct = TRUE;
                    emit_nodes(parm, NULL, TRUE, inloop);
                } else if (r0 && parm->type == NODE_LVAL) {
                    struct lval_node *q = AS_LVAL(parm);
                    args[i] = create_address_str(q->name);
                    deref0 = 1;
                    if (q->deref) {
                        deref0++;
                    }
                } else {
                    args[i] = new_name("var");
                    emit_nodes(parm, args[i], FALSE, inloop);
                    make_symbol_used(args[i]);
                }
            }
            func = p->func;
            if (retval && (p->attr & ATTRIB_NORETURN)) {
                cry("function '%s' does not return\n", func);
            }
            if (!is_loadable_sym(func)) {
                char *ptr = new_name("ptr");
                emit_load_indirect(func, FALSE);
                add_symbol_forward(ptr, 0);
                emit_store_indirect(ptr);
                func = ptr;
            } else {
                func = xstrdup(func);
            }
            make_symbol_used(func);
            if (!(p->attr & ATTRIB_NORETURN)) {
                if ((p->attr & ATTRIB_STACK) || inloop) {
                    if (!(p->attr & ATTRIB_STACK)) {
                        cry("reserved [[stack]] for '%s' because of loop\n", func);
                    }
                    mark_all_used(PROTECTED);
                } else {
                    mark_all_used(CLOBBERED);
                }
            }
            if (direct) {
                emit_call(func, NULL, 0, deref0, inloop, retval, p->attr);
            } else {
                emit_call(func, args, i, deref0, inloop, retval, p->attr);
            }
            free(func);
            while (--i >= 0) {
                free(args[i]);
            }
            break;
        }
        case NODE_ADD: {
            struct node *term;
            struct node *prev;
            int deref0 = 0;
            char *prev_tmp;
            prev = AS_ADD(n)->list;
            if (prev->type == NODE_IMM) {
                prev_tmp = xstrdup(AS_IMM(prev)->value);
                maybe_symbol_forward(prev_tmp);
            } else if (prev->type == NODE_LVAL && is_loadable(prev, TRUE)) {
                prev_tmp = xstrdup(AS_LVAL(prev)->name);
                make_symbol_used(prev_tmp);
                if (AS_LVAL(prev)->deref) {
                    deref0 = TRUE;
                }
            } else if (prev->type == NODE_LVAL) {
                prev_tmp = create_address_str(AS_LVAL(prev)->name);
                deref0 = 1;
                if (AS_LVAL(prev)->deref) {
                    deref0++;
                }
            } else {
                prev_tmp = new_name("var");
                emit_nodes(prev, prev_tmp, FALSE, inloop);
                make_symbol_used(prev_tmp);
            }
            for (term = prev->next; term; term = term->next) {
                BOOL swap = FALSE;
                char *tmp;
                char *sum = new_name("sum");
                if (term->type == NODE_IMM) {
                    tmp = xstrdup(AS_IMM(term)->value);
                    maybe_symbol_forward(tmp);
                } else if (term->type == NODE_LVAL && is_loadable(term, !deref0)) {
                    tmp = xstrdup(AS_LVAL(term)->name);
                    make_symbol_used(tmp);
                    if (AS_LVAL(term)->deref) {
                        swap = TRUE;
                        deref0 = 1;
                    }
                } else if (term->type == NODE_LVAL && !deref0) {
                    tmp = create_address_str(AS_LVAL(term)->name);
                    deref0 = 1;
                    if (AS_LVAL(term)->deref) {
                        swap = TRUE;
                        deref0++;
                    }
                } else {
                    tmp = new_name("var");
                    emit_nodes(term, tmp, FALSE, inloop);
                    make_symbol_used(tmp);
                }
                emit_add(prev_tmp, tmp, deref0, swap);
                deref0 = 0;
                if (term->next) {
                    emit_store_indirect(sum);
                }
                free(prev_tmp);
                prev_tmp = sum;
                free(tmp);
            }
            free(prev_tmp);
            break;
        }
    }
    if (assignto) {
        add_symbol_forward(assignto, 0);
        emit_store_indirect(assignto);
    } else {
        BOOL loaded = FALSE;
        for (n = n->next; n; n = n->next) {
            BOOL later = FALSE;
            struct lval_node *p = AS_LVAL(n);
            assert(n->type == NODE_LVAL);
            if (fast) {
                if (optimize_imm && !p->deref && !get_symbol(p->name) && ((p->attr & ATTRIB_CONSTANT) || !inloop)) {
                    emit_fast(p->name, fast);
                    add_symbol_defined(p->name, fast, p->attr);
                    continue;
                }
                if (!loaded) {
                    loaded = TRUE;
                    if (p->deref && !is_loadable_sym(p->name)) {
                        later = TRUE;
                    } else {
                        emit_load_direct(fast, FALSE);
                    }
                }
            }
            if (p->attr & ATTRIB_CONSTANT) {
                cry("useless const for '%s'\n", p->name);
            }
            if (p->deref) {
                /* XXX only addresses (imports/vectors) can be derefed */
                if (!is_loadable_sym(p->name)) {
                    /* XXX ok, this is very very shitty
                     * tip1: store value to tmp_N for each future p->deref at once
                     * tip2: calculate in advance how many derefs we will need and store pointers before calculating r0 (see above)
                     */
                    char *ptr = new_name("ptr");
                    char *tmp;
                    if (!later) {
                        tmp = emit_save();
                    }
                    emit_load_indirect(p->name, FALSE);
                    emit_store_indirect(ptr);
                    if (!later) {
                        emit_restore(tmp);
                    } else {
                        emit_load_direct(fast, FALSE);
                    }
                    add_symbol_forward(ptr, 0);
                    make_symbol_used(ptr);
                    emit_store_direct(ptr);
                    free(ptr);
                } else {
                    make_symbol_used(p->name);
                    emit_store_direct(p->name);
                }
            } else {
                add_symbol_forward(p->name, p->attr);
                if (try_symbol_extern(p->name)) {
                    die("cannot assign to import address '%s'\n", p->name);
                }
                if (optimize_imm && (try_symbol_attr(p->name) & ATTRIB_CONSTANT)) {
                    die("'%s' was declared constant\n", p->name);
                }
                emit_store_indirect(p->name);
            }
        }
        if (force && fast && !loaded) {
            emit_load_direct(fast, FALSE);
        }
    }
}