示例#1
0
/* input function: DCC SERVER received some data.. */
static void dcc_server_input(SERVER_DCC_REC *dcc)
{
	char tmpbuf[512], *str;
	int recvlen, ret;

	g_return_if_fail(IS_DCC_SERVER(dcc));

	do {
		recvlen = net_receive(dcc->handle, tmpbuf, sizeof(tmpbuf));

		ret = line_split(tmpbuf, recvlen, &str, &dcc->readbuf);
		if (ret == -1) {
			/* connection lost */
			dcc_close(DCC(dcc));
			break;
		}

		if (ret > 0) {
			dcc->transfd += ret;
			signal_emit("dcc server message", 2, dcc, str);
		}

		if (dcc->connection_established) {
			/* We set handle to NULL first because the new (chat/get) is using the same */
			/* handle and we don't want dcc_close to disconnect it.*/
			dcc->handle = NULL;
			dcc_close(DCC(dcc));
			break;
		}
	} while (ret > 0);
}
示例#2
0
/* SYNTAX: CAT <file> */
static void cmd_cat(const char *data)
{
	LINEBUF_REC *buffer = NULL;
	char *fname, *fposstr;
	char tmpbuf[1024], *str;
	void *free_arg;
	int f, ret, recvlen, fpos;

	if (!cmd_get_params(data, &free_arg, 2, &fname, &fposstr))
		return;

	fname = convert_home(fname);
	fpos = atoi(fposstr);
        cmd_params_free(free_arg);

	f = open(fname, O_RDONLY);
	g_free(fname);

	if (f == -1) {
		/* file not found */
                printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "%s", g_strerror(errno));
		return;
	}

        lseek(f, fpos, SEEK_SET);
	do {
		recvlen = read(f, tmpbuf, sizeof(tmpbuf));

		ret = line_split(tmpbuf, recvlen, &str, &buffer);
		if (ret > 0) printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s", str);
	} while (ret > 0);
	line_split_free(buffer);

	close(f);
}
示例#3
0
static void sig_bot_read(BOT_REC *bot)
{
	BOTNET_REC *botnet;
	char tmpbuf[1024], *str;
	int ret, recvlen, reconnect;

	botnet = bot->botnet;
	for (;;) {
		recvlen = bot->handle == -1 ? -1 :
			net_receive(bot->handle, tmpbuf, sizeof(tmpbuf));
		ret = line_split(tmpbuf, recvlen, &str, (LINEBUF_REC **) &bot->buffer);

		if (ret == 0)
			break;
		if (ret == -1) {
			/* connection lost */
                        reconnect = !bot->disconnect && bot->uplink;
			bot_destroy(bot);

			if (reconnect) {
				/* wasn't intentional disconnection from
				   our uplink, reconnect */
				botnet_connect(botnet);
			}
			break;
		}

                fprintf(stderr, "%s\r\n", str);
		signal_emit("botnet event", 2, bot, str);
	}
}
示例#4
0
static void sig_exec_input_reader(PROCESS_REC *rec)
{
        char tmpbuf[512], *str;
        unsigned int recvlen;
	int err, ret;

	g_return_if_fail(rec != NULL);

	recvlen = 0;
	err = g_io_channel_read(rec->in, tmpbuf,
				sizeof(tmpbuf), &recvlen);
	if (err != 0 && err != G_IO_ERROR_AGAIN && errno != EINTR)
		recvlen = -1;

	do {
		ret = line_split(tmpbuf, recvlen, &str, &rec->databuf);
		if (ret == -1) {
			/* link to terminal closed? */
			g_source_remove(rec->read_tag);
                        rec->read_tag = -1;
			break;
		}

		if (ret > 0) {
			signal_emit_id(signal_exec_input, 2, rec, str);
                        if (recvlen > 0) recvlen = 0;
		}
	} while (ret > 0);
}
示例#5
0
static int show_help_rec(COMMAND_REC *cmd)
{
    char tmpbuf[1024], *str, *path;
    LINEBUF_REC *buffer = NULL;
    int f, ret, recvlen;

    /* helpdir/command or helpdir/category/command */
    if (cmd->category == NULL)
	path = g_strdup_printf("%s/%s", HELPDIR, cmd->cmd);
    else
	path = g_strdup_printf("%s/%s/%s", HELPDIR, cmd->category, cmd->cmd);
    f = open(path, O_RDONLY);
    g_free(path);

    if (f == -1)
	return FALSE;

    /* just print to screen whatever is in the file */
    do
    {
	recvlen = read(f, tmpbuf, sizeof(tmpbuf));

	ret = line_split(tmpbuf, recvlen, &str, &buffer);
        if (ret > 0) printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, str);
    }
    while (ret > 0);
    line_split_free(buffer);

    close(f);
    return TRUE;
}
示例#6
0
static void sig_pidwait(void *pid, void *statusp)
{
	PROCESS_REC *rec;
        char *str;
	int status = GPOINTER_TO_INT(statusp);

        rec = process_find_pid(GPOINTER_TO_INT(pid));
	if (rec == NULL) return;

	/* process exited - print the last line if
	   there wasn't a newline at end. */
	if (line_split("\n", 1, &str, &rec->databuf) > 0 && *str != '\0')
		signal_emit_id(signal_exec_input, 2, rec, str);

	if (!rec->silent) {
		if (WIFSIGNALED(status)) {
			status = WTERMSIG(status);
			printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
				  "process %d (%s) terminated with signal %d (%s)",
				  rec->id, rec->args,
				  status, g_strsignal(status));
		} else {
                        status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
			printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
				  "process %d (%s) terminated with return code %d",
				  rec->id, rec->args, status);
		}
	}
	process_destroy(rec, status);
}
示例#7
0
文件: ldapproxy.c 项目: Zabrane/SPOCP
/* expects 
 * "what" +SP "=" +SP <base> "/" attr *( ',' attr ) in the configuration
 * file
 */
spocp_result_t 
set_attr( void **vpp, void *cd, int argc, char **argv)
{
	ainfo_t *ai=0, *newai ;
	char	*sp;
	char	*buf;
	int	n;
	LDAPDN	dn = NULL;
	void	*vp;

	traceLog( LOG_DEBUG, "argc: %d, argv[0]: %s", argc, argv[0]);
	if( argc != 1 )
		return SPOCP_PARAM_ERROR;


	buf = normalize( argv[0]) ;
	sp = index( buf, '/' );

	if ( !sp)
		return SPOCP_PARAM_ERROR;

	*sp++ = '\0';

	newai = ( ainfo_t * ) calloc (1, sizeof( ainfo_t ));

	traceLog( LOG_DEBUG, "Base DN[0]: %s", buf );
	ldap_str2dn( buf, &dn, LDAP_DN_FORMAT_LDAPV3);
	ldap_dn2str( dn, &buf, LDAP_DN_FORMAT_LDAPV3);

	ldap_memfree( dn );

	traceLog( LOG_DEBUG, "Base DN[1]: %s", buf);
	newai->base = buf;
	newai->attr = line_split( sp, ',', 0, 1, 0, &n);

	vp = (void *) newai;

	if( *vpp != 0 ) {
		ai = (ainfo_t *) *vpp;
		newai->next = ai;
	}

	*vpp = vp;

	return SPOCP_SUCCESS;
}
示例#8
0
std::list<Token> Lexer::GetTokens(const std::string& a) {
	line_count = 0;
	locs.clear();
	std::list<std::string> lines = line_split(a);
	std::list<Token> ret;
	for (auto& line : lines) {
		if (line == "\n") continue;
		count = 1;
		line_count++;
		std::list<std::string> tmp = split(line);
		int cc = 0;
		for (auto& t : tmp) {
			if (is_keyword(t)) {
				std::for_each(t.begin(), t.end(), to_lower);
				ret.push_back(Token(KEYWORD, t, locs[cc] - t.size(), line_count));
			}
			else if (ops.find(t) != ops.end()) {
				ret.push_back(Token(OP, t, locs[cc] - t.size(), line_count));
			}
			else if (t == " "){
			}
			else {
				//NUM
				if (t[0] >= '0' && t[0] <= '9') {
					for (int i = 1; i < t.size(); i++) {
						if (!(t[i] >= '0' && t[i] <= '9')) {
							throw(SDBException("Illegal Tokens"));
						}
					}
					ret.push_back(Token(NUM, t, locs[cc] - t.size(), line_count));
					//ID
				}
				else if ((t[0] >= 'a' && t[0] <= 'z')
					|| (t[0] >= 'A' && t[0] <= 'Z') || t[0] == '_') {
					if(t.size() >= MAX_ID_LEN) {
						throw(SDBException("Token Length Too Long"));
					}
					ret.push_back(Token(ID, t, locs[cc] - t.size(), line_count));
				}
			}
			cc++;
		}
	}
	return ret;
}
示例#9
0
/* input function: DCC CHAT received some data.. */
static void dcc_chat_input(DCC_REC *dcc)
{
        char tmpbuf[512], *str;
	int recvlen, ret;

	g_return_if_fail(dcc != NULL);

	do {
		recvlen = net_receive(dcc->handle, tmpbuf, sizeof(tmpbuf));

		ret = line_split(tmpbuf, recvlen, &str, (LINEBUF_REC **) &dcc->databuf);
		if (ret == -1) {
			/* connection lost */
			signal_emit("dcc closed", 1, dcc);
			dcc_destroy(dcc);
			break;
		}

		if (ret > 0) {
			dcc->transfd += ret;
			signal_emit("dcc chat message", 2, dcc, str);
		}
	} while (ret > 0);
}
示例#10
0
文件: ldapproxy.c 项目: Zabrane/SPOCP
static char *
fqdn2dn( char *fqdn )
{
	int	n, i, len;
	char	**arr, *dn, *tp;

	arr = line_split( fqdn, '.', 0, 0, 0, &n );

	for( i=0, len=0; arr[i]; i++) len += strlen(arr[i]) + 4 ;

	dn = ( char *) calloc (len+1, sizeof( char ));

	for( i=0, tp=dn; arr[i]; i++ ) {
		sprintf( tp, "dc=%s,", arr[i]);
		tp += strlen( tp);
	}
	--tp;
	*tp = '\0';

	for( i=0; arr[i]; i++ ) free( arr[i] );
	free(arr);

	return dn;
}
示例#11
0
static void
_pp_host_get_snmp_devices_thread (GSimpleAsyncResult *res,
                                  GObject            *object,
                                  GCancellable       *cancellable)
{
  PpHost         *host = (PpHost *) object;
  PpHostPrivate  *priv = host->priv;
  PpPrintDevice  *device;
  GSDData        *data;
  GError         *error;
  gchar         **argv;
  gchar          *stdout_string = NULL;
  gchar          *stderr_string = NULL;
  gint            exit_status;

  data = g_simple_async_result_get_op_res_gpointer (res);
  data->devices = g_new0 (PpDevicesList, 1);
  data->devices->devices = NULL;

  argv = g_new0 (gchar *, 3);
  argv[0] = g_strdup ("/usr/lib/cups/backend/snmp");
  argv[1] = g_strdup (priv->hostname);

  /* Use SNMP to get printer's informations */
  g_spawn_sync (NULL,
                argv,
                NULL,
                0,
                NULL,
                NULL,
                &stdout_string,
                &stderr_string,
                &exit_status,
                &error);

  g_free (argv[1]);
  g_free (argv[0]);
  g_free (argv);

  if (exit_status == 0 && stdout_string)
    {
      gchar **printer_informations = NULL;
      gint    length;

      printer_informations = line_split (stdout_string);
      length = g_strv_length (printer_informations);

      if (length >= 4)
        {
          device = g_new0 (PpPrintDevice, 1);

          device->device_class = g_strdup (printer_informations[0]);
          device->device_uri = g_strdup (printer_informations[1]);
          device->device_make_and_model = g_strdup (printer_informations[2]);
          device->device_info = g_strdup (printer_informations[3]);
          device->device_name = g_strdup (printer_informations[3]);
          device->device_name =
            g_strcanon (device->device_name, ALLOWED_CHARACTERS, '-');
          device->acquisition_method = ACQUISITION_METHOD_SNMP;

          if (length >= 5 && printer_informations[4][0] != '\0')
            device->device_id = g_strdup (printer_informations[4]);

          if (length >= 6 && printer_informations[5][0] != '\0')
            device->device_location = g_strdup (printer_informations[5]);

          data->devices->devices = g_list_append (data->devices->devices, device);
        }

      g_strfreev (printer_informations);
      g_free (stdout_string);
    }
}
示例#12
0
文件: listen.c 项目: svn2github/irssi
static void sig_listen_client(CLIENT_REC *client, gint handle)
{
    char tmpbuf[1024], *str, *cmd, *args, *p;
    int ret, recvlen;

    g_return_if_fail(client != NULL);

    for (;;)
    {
	recvlen = net_receive(handle, tmpbuf, sizeof(tmpbuf));
	ret = line_split(tmpbuf, recvlen, &str, &client->buffer);
        if (ret == -1)
        {
            /* connection lost */
            remove_client(proxy_data, client);
            break;
        }
	if (ret == 0) break;

	if (client->server == NULL)
	    continue;

	cmd = g_strdup(str);
	args = strchr(cmd, ' ');
	if (args != NULL) *args++ = '\0'; else args = "";
	if (*args == ':') args++;
	g_strup(cmd);

	if (!client->connected)
	{
	    if (proxy_data->password != NULL && strcmp(cmd, "PASS") == 0)
	    {
		if (strcmp(proxy_data->password, args) != 0)
		{
		    /* wrong password! */
		    remove_client(proxy_data, client);
                    break;
		}
		client->pass_sent = TRUE;
	    }
	    else if (strcmp(cmd, "NICK") == 0)
		client->nick = g_strdup(args);
	    else if (strcmp(cmd, "USER") == 0)
	    {
		if (client->nick == NULL || (proxy_data->password != NULL && !client->pass_sent))
		{
		    /* stupid client didn't send us NICK/PASS or, kill it */
		    remove_client(proxy_data, client);
		    break;
		}
		client->connected = TRUE;
		plugin_proxy_dump_data(client);
	    }
	}
        else if (strcmp(cmd, "QUIT") == 0)
        {
            remove_client(proxy_data, client);
            break;
        }
	else if (strcmp(cmd, "PING") == 0)
	{
	    net_transmit(handle, "PONG proxy :nick\n", 17);
	}
	else
	{
	    net_transmit(net_sendbuffer_handle(client->server->handle), str, strlen(str));
	    net_transmit(net_sendbuffer_handle(client->server->handle), "\n", 1);

	    if (strcmp(cmd, "WHO") == 0)
	    {
		grab_who(client, args);
	    }
	    else if (strcmp(cmd, "WHOIS") == 0)
	    {
		/* convert dots to spaces */
		for (p = args; *p != '\0'; p++)
		    if (*p == ',') *p = ' ';

		proxy_redirect_event(client, args, 2,
				     "event 318", -1, "event 402", -1,
				     "event 401", 1, "event 311", 1,
				     "event 301", 1, "event 312", 1,
				     "event 313", 1, "event 317", 1,
				     "event 319", 1, NULL);
	    }
	    else if (strcmp(cmd, "ISON") == 0)
	    {
		proxy_redirect_event(client, NULL, 1, "event 303", -1, NULL);
	    }
	    else if (strcmp(cmd, "USERHOST") == 0)
	    {
		proxy_redirect_event(client, args, 1, "event 302", -1, "event 401", 1, NULL);
	    }
	    else if (strcmp(cmd, "MODE") == 0)
	    {
		/* convert dots to spaces */
		gchar *slist, *str, mode;
		gint argc;

		p = strchr(args, ' ');
		if (p != NULL) *p++ = '\0';
		mode = p == NULL ? '\0' : *p;

		slist = g_strdup(args);
		argc = 1;
		for (p = slist; *p != '\0'; p++)
		{
		    if (*p == ',')
		    {
			*p = ' ';
			argc++;
		    }
		}

		/* get channel mode / bans / exception / invite list */
		str = g_strdup_printf("%s %s", args, slist);
		switch (mode)
		{
		    case '\0':
                        while (argc-- > 0)
			    proxy_redirect_event(client, str, 3, "event 403", 1,
						 "event 443", 1, "event 324", 1, NULL);
			break;
		    case 'b':
                        while (argc-- > 0)
			    proxy_redirect_event(client, str, 2, "event 403", 1,
						 "event 368", 1, "event 367", 1, NULL);
			break;
		    case 'e':
			while (argc-- > 0)
			    proxy_redirect_event(client, str, 4, "event 403", 1,
						 "event 482", 1, "event 472", -1,
						 "event 349", 1, "event 348", 1, NULL);
			break;
		    case 'I':
                        while (argc-- > 0)
			    proxy_redirect_event(client, str, 4, "event 403", 1,
						 "event 482", 1, "event 472", -1,
						 "event 347", 1, "event 346", 1, NULL);
			break;
		}
		g_free(str);
		g_free(slist);
	    }
	}
	g_free(cmd);
    }
}
// menu_action: Called when a menu/toolbar item is selected
// ----------------------------------------------------- >>
static void menu_action(GtkAction *action)
{
	string act = gtk_action_get_name(action);

	if (act == "WadManager")
		open_main_window();
	else if (act == "Exit")
		gtk_main_quit();
	else if (act == "Close")
		file_close();
	else if (act == "ModeVerts")
		change_edit_mode(0);
	else if (act == "ModeLines")
		change_edit_mode(1);
	else if (act == "ModeSectors")
		change_edit_mode(2);
	else if (act == "ModeThings")
		change_edit_mode(3);
	else if (act == "Mode3d")
	{
		if (map.opened)
			start_3d_mode();
	}
	else if (act == "ShowConsole")
		popup_console();
	else if (act == "ShowScriptEditor")
		open_script_edit();
	else if (act == "Preferences")
		open_prefs_dialog();
	else if (act == "About")
	{
		gtk_show_about_dialog(GTK_WINDOW(editor_window),
								"name", "SLADE",
								"comments", "by Simon 'SlayeR' Judd, 2005",
								"version", __SLADEVERS,
								"website", "http://slade.mancubus.net",
								NULL);
	}
	else if (act == "Save")
	{
		if (edit_wad)
			file_save();
		else
			file_saveas();
	}
	else if (act == "SaveAs")
		file_saveas();
	else if (act == "MergeSectors")
		sector_merge(false);
	else if (act == "JoinSectors")
		sector_merge(true);
	else if (act == "CreateDoor")
		sector_create_door(open_texture_browser(true, false, false, "-"));
	else if (act == "CreateStairs")
		edit_create_stairs();
	else if (act == "CheckMapStats")
		check_map_stats();
	else if (act == "CheckTags")
		message_box(parse_string("%d Tags Cleaned", clean_tags()), GTK_MESSAGE_INFO);
	else if (act == "CheckVerts")
		message_box(parse_string("%d Vertices Removed", remove_free_verts()), GTK_MESSAGE_INFO);
	else if (act == "CheckLines")
		message_box(parse_string("%d 0-Length Lines Removed", remove_zerolength_lines()), GTK_MESSAGE_INFO);
	else if (act == "CheckSectors")
		message_box(parse_string("%d Sectors Removed", remove_unused_sectors()), GTK_MESSAGE_INFO);
	else if (act == "CheckTextures")
		check_textures();
	else if (act == "AlignX")
		line_align_x();
	else if (act == "CorrectRefs")
		line_correct_references();
	else if (act == "SplitLine")
	{
		int splits = 2;
		string ret = entry_box("Enter Number Of Splits:");

		if (ret != "")
			splits = atoi(ret.c_str());

		line_split(splits);
	}
	else
		message_box("Menu action not implemented", GTK_MESSAGE_INFO);

	force_map_redraw(true, true);
}
示例#14
0
Tokens jsonnet_lex(const std::string &filename, const char *input)
{
    unsigned long line_number = 1;
    const char *line_start = input;

    Tokens r;

    const char *c = input;

    Fodder fodder;
    bool fresh_line = true;  // Are we tokenizing from the beginning of a new line?

    while (*c!='\0') {
        Token::Kind kind;
        std::string data;
        std::string string_block_indent;
        std::string string_block_term_indent;

        unsigned new_lines, indent;
        lex_ws(c, new_lines, indent, line_start, line_number);

        // If it's the end of the file, discard final whitespace.
        if (*c == '\0')
            break;

        if (new_lines > 0) {
            // Otherwise store whitespace in fodder.
            unsigned blanks = new_lines - 1;
            fodder.emplace_back(FodderElement::LINE_END, blanks, indent, EMPTY);
            fresh_line = true;
        }

        Location begin(line_number, c - line_start + 1);

        switch (*c) {

            // The following operators should never be combined with subsequent symbols.
            case '{':
            kind = Token::BRACE_L;
            c++;
            break;

            case '}':
            kind = Token::BRACE_R;
            c++;
            break;

            case '[':
            kind = Token::BRACKET_L;
            c++;
            break;

            case ']':
            kind = Token::BRACKET_R;
            c++;
            break;

            case ',':
            kind = Token::COMMA;
            c++;
            break;

            case '.':
            kind = Token::DOT;
            c++;
            break;

            case '(':
            kind = Token::PAREN_L;
            c++;
            break;

            case ')':
            kind = Token::PAREN_R;
            c++;
            break;

            case ';':
            kind = Token::SEMICOLON;
            c++;
            break;

            // Numeric literals.
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            kind = Token::NUMBER;
            data = lex_number(c, filename, begin);
            break;

            // String literals.
            case '"': {
                c++;
                for (; ; ++c) {
                    if (*c == '\0') {
                        throw StaticError(filename, begin, "Unterminated string");
                    }
                    if (*c == '"') {
                        break;
                    }
                    if (*c == '\\' && *(c+1) != '\0') {
                        data += *c;
                        ++c;
                    }
                    if (*c == '\n') {
                        // Maintain line/column counters.
                        line_number++;
                        line_start = c+1;
                    }
                    data += *c;
                }
                c++;  // Advance beyond the ".
                kind = Token::STRING_DOUBLE;
            }
            break;

            // String literals.
            case '\'': {
                c++;
                for (; ; ++c) {
                    if (*c == '\0') {
                        throw StaticError(filename, begin, "Unterminated string");
                    }
                    if (*c == '\'') {
                        break;
                    }
                    if (*c == '\\' && *(c+1) != '\0') {
                        data += *c;
                        ++c;
                    }
                    if (*c == '\n') {
                        // Maintain line/column counters.
                        line_number++;
                        line_start = c+1;
                    }
                    data += *c;
                }
                c++;  // Advance beyond the '.
                kind = Token::STRING_SINGLE;
            }
            break;

            // Keywords
            default:
            if (is_identifier_first(*c)) {
                std::string id;
                for (; is_identifier(*c); ++c)
                    id += *c;
                if (id == "assert") {
                    kind = Token::ASSERT;
                } else if (id == "else") {
                    kind = Token::ELSE;
                } else if (id == "error") {
                    kind = Token::ERROR;
                } else if (id == "false") {
                    kind = Token::FALSE;
                } else if (id == "for") {
                    kind = Token::FOR;
                } else if (id == "function") {
                    kind = Token::FUNCTION;
                } else if (id == "if") {
                    kind = Token::IF;
                } else if (id == "import") {
                    kind = Token::IMPORT;
                } else if (id == "importstr") {
                    kind = Token::IMPORTSTR;
                } else if (id == "in") {
                    kind = Token::IN;
                } else if (id == "local") {
                    kind = Token::LOCAL;
                } else if (id == "null") {
                    kind = Token::NULL_LIT;
                } else if (id == "self") {
                    kind = Token::SELF;
                } else if (id == "super") {
                    kind = Token::SUPER;
                } else if (id == "tailstrict") {
                    kind = Token::TAILSTRICT;
                } else if (id == "then") {
                    kind = Token::THEN;
                } else if (id == "true") {
                    kind = Token::TRUE;
                } else {
                    // Not a keyword, must be an identifier.
                    kind = Token::IDENTIFIER;
                }
                data = id;

            } else if (is_symbol(*c) || *c == '#') {

                // Single line C++ and Python style comments.
                if (*c == '#' || (*c == '/' && *(c+1) == '/')) {
                    std::vector<std::string> comment(1);
                    unsigned blanks;
                    unsigned indent;
                    lex_until_newline(c, comment[0], blanks, indent, line_start, line_number);
                    auto kind = fresh_line ? FodderElement::PARAGRAPH : FodderElement::LINE_END;
                    fodder.emplace_back(kind, blanks, indent, comment);
                    fresh_line = true;
                    continue;  // We've not got a token, just fodder, so keep scanning.
                }

                // Multi-line C style comment.
                if (*c == '/' && *(c+1) == '*') {

                    unsigned margin = c - line_start;
 
                    const char *initial_c = c;
                    c += 2;  // Avoid matching /*/: skip the /* before starting the search for */.

                    while (!(*c == '*' && *(c+1) == '/')) {
                        if (*c == '\0') {
                            auto msg = "Multi-line comment has no terminating */.";
                            throw StaticError(filename, begin, msg);
                        }
                        if (*c == '\n') {
                            // Just keep track of the line / column counters.
                            line_number++;
                            line_start = c+1;
                        }
                        ++c;
                    }
                    c += 2;  // Move the pointer to the char after the closing '/'.

                    std::string comment(initial_c, c - initial_c);  // Includes the "/*" and "*/".

                    // Lex whitespace after comment
                    unsigned new_lines_after, indent_after;
                    lex_ws(c, new_lines_after, indent_after, line_start, line_number);
                    std::vector<std::string> lines;
                    if (comment.find('\n') >= comment.length()) {
                        // Comment looks like /* foo */
                        lines.push_back(comment);
                        fodder.emplace_back(FodderElement::INTERSTITIAL, 0, 0, lines);
                        if (new_lines_after > 0) {
                            fodder.emplace_back(FodderElement::LINE_END, new_lines_after - 1,
                                                indent_after, EMPTY);
                            fresh_line = true;
                        }
                    } else {
                        lines = line_split(comment, margin);
                        assert(lines[0][0] == '/');
                        // Little hack to support PARAGRAPHs with * down the LHS:
                        // Add a space to lines that start with a '*'
                        bool all_star = true;
                        for (auto &l : lines) {
                            if (l[0] != '*')
                                all_star = false;
                        }
                        if (all_star) {
                            for (auto &l : lines) {
                                if (l[0] == '*') l = " " + l;
                            }
                        }
                        if (new_lines_after == 0) {
                            // Ensure a line end after the paragraph.
                            new_lines_after = 1;
                            indent_after = 0;
                        }
                        if (!fresh_line)
                            // Ensure a line end before the comment.
                            fodder.emplace_back(FodderElement::LINE_END, 0, 0, EMPTY);
                        fodder.emplace_back(FodderElement::PARAGRAPH, new_lines_after - 1,
                                            indent_after, lines);
                        fresh_line = true;
                    }
                    continue;  // We've not got a token, just fodder, so keep scanning.
                }

                // Text block
                if (*c == '|' && *(c+1) == '|' && *(c+2) == '|' && *(c+3) == '\n') {
                    std::stringstream block;
                    c += 4; // Skip the "|||\n"
                    line_number++;
                    // Skip any blank lines at the beginning of the block.
                    while (*c == '\n') {
                        line_number++;
                        ++c;
                        block << '\n';
                    }
                    line_start = c;
                    const char *first_line = c;
                    int ws_chars = whitespace_check(first_line, c);
                    string_block_indent = std::string(first_line, ws_chars);
                    if (ws_chars == 0) {
                        auto msg = "Text block's first line must start with whitespace.";
                        throw StaticError(filename, begin, msg);
                    }
                    while (true) {
                        assert(ws_chars > 0);
                        // Read up to the \n
                        for (c = &c[ws_chars]; *c != '\n' ; ++c) {
                            if (*c == '\0')
                                throw StaticError(filename, begin, "Unexpected EOF");
                            block << *c;
                        }
                        // Add the \n
                        block << '\n';
                        ++c;
                        line_number++;
                        line_start = c;
                        // Skip any blank lines
                        while (*c == '\n') {
                            line_number++;
                            ++c;
                            block << '\n';
                        }
                        // Examine next line
                        ws_chars = whitespace_check(first_line, c);
                        if (ws_chars == 0) {
                            // End of text block
                            // Skip over any whitespace
                            while (*c == ' ' || *c == '\t') {
                                string_block_term_indent += *c;
                                ++c;
                            }
                            // Expect |||
                            if (!(*c == '|' && *(c+1) == '|' && *(c+2) == '|')) {
                                auto msg = "Text block not terminated with |||";
                                throw StaticError(filename, begin, msg);
                            }
                            c += 3;  // Leave after the last |
                            data = block.str();
                            kind = Token::STRING_BLOCK;
                            break;  // Out of the while loop.
                        }
                    }

                    break;  // Out of the switch.
                }

                const char *operator_begin = c;
                for (; is_symbol(*c) ; ++c) {
                    // Not allowed // in operators
                    if (*c == '/' && *(c+1) == '/') break;
                    // Not allowed /* in operators
                    if (*c == '/' && *(c+1) == '*') break;
                    // Not allowed ||| in operators
                    if (*c == '|' && *(c+1) == '|' && *(c+2) == '|') break;
                }
                // Not allowed to end with a + - ~ ! unless a single char.
                // So, wind it back if we need to (but not too far).
                while (c > operator_begin + 1
                       && (*(c-1) == '+' || *(c-1) == '-' || *(c-1) == '~' || *(c-1) == '!')) {
                    c--;
                }
                data += std::string(operator_begin, c);
                if (data == "$") {
                    kind = Token::DOLLAR;
                    data = "";
                } else {
                    kind = Token::OPERATOR;
                }
            } else {
                std::stringstream ss;
                ss << "Could not lex the character ";
                auto uc = (unsigned char)(*c);
                if (*c < 32)
                    ss << "code " << unsigned(uc);
                else
                    ss << "'" << *c << "'";
                throw StaticError(filename, begin, ss.str());
            }
        }

        Location end(line_number, c - line_start);
        r.emplace_back(kind, fodder, data, string_block_indent, string_block_term_indent,
                       LocationRange(filename, begin, end));
        fodder.clear();
        fresh_line = false;
    }

    Location end(line_number, c - line_start + 1);
    r.emplace_back(Token::END_OF_FILE, fodder, "", "", "", LocationRange(filename, end, end));
    return r;
}