示例#1
0
void parse_string(char instruction[ ], int size)
{
	int ind;
	int len;	/* length of parameter argument */

	for(int i=0; i<GCODE_COUNT;i++)
		gc.seen[i] = false;

	len=0;
	/* scan the string for commands and parameters, recording the arguments for each,
	 * and setting the seen flag for each that is seen
	 */
	for (ind=0; ind<size; ind += (1+len))
	{
		len = 0;
		switch (instruction[ind])
		{
			case 'G':
					if(gc.GIndex<kMaxGCommands)
						len = scan_int(&instruction[ind+1], &(gc.G[gc.GIndex++]), gc.seen, GCODE_G);
					else
					{
						sprintf(talkToHost.string(), "Too many G codes per line");
						talkToHost.setFatal();
						talkToHost.sendMessage(true);
					}
					break;
			PARSE_INT('M', &instruction[ind+1], len, gc.M, gc.seen, GCODE_M);
			PARSE_INT('T', &instruction[ind+1], len, gc.T, gc.seen, GCODE_T);
			PARSE_INT('L', &instruction[ind+1], len, gc.L, gc.seen, GCODE_L);
			PARSE_FLOAT('S', &instruction[ind+1], len, gc.S, gc.seen, GCODE_S);
			PARSE_FLOAT('P', &instruction[ind+1], len, gc.P, gc.seen, GCODE_P);
			PARSE_FLOAT('X', &instruction[ind+1], len, gc.X, gc.seen, GCODE_X);
			PARSE_FLOAT('Y', &instruction[ind+1], len, gc.Y, gc.seen, GCODE_Y);
			PARSE_FLOAT('Z', &instruction[ind+1], len, gc.Z, gc.seen, GCODE_Z);
			PARSE_FLOAT('I', &instruction[ind+1], len, gc.I, gc.seen, GCODE_I);
			PARSE_FLOAT('J', &instruction[ind+1], len, gc.J, gc.seen, GCODE_J);
			PARSE_FLOAT('F', &instruction[ind+1], len, gc.F, gc.seen, GCODE_F);
			PARSE_FLOAT('R', &instruction[ind+1], len, gc.R, gc.seen, GCODE_R);
			PARSE_FLOAT('Q', &instruction[ind+1], len, gc.Q, gc.seen, GCODE_Q);
			PARSE_FLOAT('E', &instruction[ind+1], len, gc.A, gc.seen, GCODE_E);
			PARSE_FLOAT('A', &instruction[ind+1], len, gc.A, gc.seen, GCODE_A);
			PARSE_FLOAT('B', &instruction[ind+1], len, gc.B, gc.seen, GCODE_B);
			PARSE_LONG('N', &instruction[ind+1], len, gc.N, gc.seen, GCODE_N);
			PARSE_INT('*', &instruction[ind+1], len, gc.Checksum, gc.seen, GCODE_CHECKSUM);
			default:
				break;
		}
	}
}
示例#2
0
void HUserMap::updateHeaven(CCArray* arr)
{
    m_heaven.clear();
    FOREACH(CCDictionary*, heaven, arr) {
        HTower tower;
        tower.id = PARSE_INT(heaven, "ID");
        tower.tower = PARSE_INT(heaven, "Tower");
        tower.layer = PARSE_INT(heaven, "CurLayer");
        tower.max_layer = PARSE_INT(heaven, "MaxLayer");
        tower.speed = PARSE_INT(heaven, "Speed")+0;
        tower.max_speed = PARSE_INT(heaven, "MaxSpeed");
        tower.challenge = PARSE_INT(heaven, "Challenge");
        tower.buy_num = PARSE_INT(heaven, "BuyNum");
        tower.max_buy_num = PARSE_INT(heaven, "MaxBuyNum");
        tower.reset_gold = PARSE_INT(heaven, "ResetGold");
        m_heaven.push_back(tower);
    }
示例#3
0
ClientOptions MainController::parseClientOptions(const std::string& optionString) {
	ClientOptions result;
	size_t i = 0;
	int intVal = 0;
	std::string stringVal;
	std::vector<std::string> segments = String::split(optionString, ',');

	PARSE_BOOL(useStreamCompression, 1);
	PARSE_INT_RAW(-1);
	switch (intVal) {
		case 1: result.useTLS = ClientOptions::NeverUseTLS;break;
		case 2: result.useTLS = ClientOptions::UseTLSWhenAvailable;break;
		case 3: result.useTLS = ClientOptions::RequireTLS;break;
		default:;
	}
	PARSE_BOOL(allowPLAINWithoutTLS, 0);
	PARSE_BOOL(useStreamResumption, 0);
	PARSE_BOOL(useAcks, 1);
	PARSE_STRING(manualHostname);
	PARSE_INT(manualPort, -1);
	PARSE_INT_RAW(-1);
	switch (intVal) {
		case 1: result.proxyType = ClientOptions::NoProxy;break;
		case 2: result.proxyType = ClientOptions::SystemConfiguredProxy;break;
		case 3: result.proxyType = ClientOptions::SOCKS5Proxy;break;
		case 4: result.proxyType = ClientOptions::HTTPConnectProxy;break;
	}
	PARSE_STRING(manualProxyHostname);
	PARSE_INT(manualProxyPort, -1);
	PARSE_URL(boshURL);
	PARSE_URL(boshHTTPConnectProxyURL);
	PARSE_SAFE_STRING(boshHTTPConnectProxyAuthID);
	PARSE_SAFE_STRING(boshHTTPConnectProxyAuthPassword);
	PARSE_BOOL(tlsOptions.schannelTLS1_0Workaround, false);

	return result;
}
示例#4
0
/* returned pointer is always within [buf, buf_end), or null */
static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
{
    /* we want at least [HTTP/1.<two chars>] to try to parse */
    if (buf_end - buf < 9) {
        *ret = -2;
        return NULL;
    }
    EXPECT_CHAR_NO_CHECK('H');
    EXPECT_CHAR_NO_CHECK('T');
    EXPECT_CHAR_NO_CHECK('T');
    EXPECT_CHAR_NO_CHECK('P');
    EXPECT_CHAR_NO_CHECK('/');
    EXPECT_CHAR_NO_CHECK('1');
    EXPECT_CHAR_NO_CHECK('.');
    PARSE_INT(minor_version, 1);
    return buf;
}
/* rtpmap contains:
 *
 *  <payload> <encoding_name>/<clock_rate>[/<encoding_params>]
 */
static gboolean
gst_sdp_parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name,
    gint * rate, gchar ** params)
{
  gchar *p, *t;

  p = (gchar *) rtpmap;

  PARSE_INT (p, " ", *payload);
  if (*payload == -1)
    return FALSE;

  SKIP_SPACES (p);
  if (*p == '\0')
    return FALSE;

  PARSE_STRING (p, "/", *name);
  if (*name == NULL) {
    GST_DEBUG ("no rate, name %s", p);
    /* no rate, assume -1 then, this is not supposed to happen but RealMedia
     * streams seem to omit the rate. */
    *name = p;
    *rate = -1;
    return TRUE;
  }

  t = p;
  p = strstr (p, "/");
  if (p == NULL) {
    *rate = atoi (t);
    return TRUE;
  }
  *p = '\0';
  p++;
  *rate = atoi (t);

  t = p;
  if (*p == '\0')
    return TRUE;
  *params = t;

  return TRUE;
}
示例#6
0
/* rtpmap contains:
 *
 *  <payload> <encoding_name>/<clock_rate>[/<encoding_params>]
 */
static gboolean
gst_sdp_demux_parse_rtpmap (const gchar * rtpmap, gint * payload, gchar ** name,
    gint * rate, gchar ** params)
{
  gchar *p, *t;

  t = p = (gchar *) rtpmap;

  PARSE_INT (p, " ", *payload);
  if (*payload == -1)
    return FALSE;

  SKIP_SPACES (p);
  if (*p == '\0')
    return FALSE;

  PARSE_STRING (p, "/", *name);
  if (*name == NULL) {
    GST_DEBUG ("no rate, name %s", p);
    /* no rate, assume -1 then */
    *name = p;
    *rate = -1;
    return TRUE;
  }

  t = p;
  p = strstr (p, "/");
  if (p == NULL) {
    *rate = atoi (t);
    return TRUE;
  }
  *p = '\0';
  p++;
  *rate = atoi (t);

  t = p;
  if (*p == '\0')
    return TRUE;
  *params = t;

  return TRUE;
}
/**
 * gst_sdp_media_get_caps_from_media:
 * @media: a #GstSDPMedia
 * @pt: a payload type
 *
 * Mapping of caps from SDP fields:
 *
 * a=rtpmap:(payload) (encoding_name)/(clock_rate)[/(encoding_params)]
 *
 * a=framesize:(payload) (width)-(height)
 *
 * a=fmtp:(payload) (param)[=(value)];...
 *
 * Returns: a #GstCaps, or %NULL if an error happened
 *
 * I got it from Gstreamer source cod, release 1.8
 */
GstCaps *
gst_sdp_media_get_caps_from_media (const GstSDPMedia * media, gint pt)
{
  GstCaps *caps;
  const gchar *rtpmap;
  const gchar *fmtp;
  const gchar *framesize;
  gchar *name = NULL;
  gint rate = -1;
  gchar *params = NULL;
  gchar *tmp;
  GstStructure *s;
  gint payload = 0;
  gboolean ret;

  /* get and parse rtpmap */
  rtpmap = gst_sdp_get_attribute_for_pt (media, "rtpmap", pt);

  if (rtpmap) {
    ret = gst_sdp_parse_rtpmap (rtpmap, &payload, &name, &rate, &params);
    if (!ret) {
      GST_ERROR ("error parsing rtpmap, ignoring");
      rtpmap = NULL;
    }
  }
  /* dynamic payloads need rtpmap or we fail */
  if (rtpmap == NULL && pt >= 96)
    goto no_rtpmap;

  /* check if we have a rate, if not, we need to look up the rate from the
   * default rates based on the payload types. */
  if (rate == -1) {
    const GstRTPPayloadInfo *info;

    if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) {
      /* dynamic types, use media and encoding_name */
      tmp = g_ascii_strdown (media->media, -1);
      info = gst_rtp_payload_info_for_name (tmp, name);
      g_free (tmp);
    } else {
      /* static types, use payload type */
      info = gst_rtp_payload_info_for_pt (pt);
    }

    if (info) {
      if ((rate = info->clock_rate) == 0)
        rate = -1;
    }
    /* we fail if we cannot find one */
    if (rate == -1)
      goto no_rate;
  }

  tmp = g_ascii_strdown (media->media, -1);
  caps = gst_caps_new_simple ("application/x-rtp",
      "media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL);
  g_free (tmp);
  s = gst_caps_get_structure (caps, 0);

  gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL);

  /* encoding name must be upper case */
  if (name != NULL) {
    tmp = g_ascii_strup (name, -1);
    gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL);
    g_free (tmp);
  }

  /* params must be lower case */
  if (params != NULL) {
    tmp = g_ascii_strdown (params, -1);
    gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL);
    g_free (tmp);
  }

  /* parse optional fmtp: field */
  if ((fmtp = gst_sdp_get_attribute_for_pt (media, "fmtp", pt))) {
    gchar *p;
    gint payload = 0;

    p = (gchar *) fmtp;

    /* p is now of the format <payload> <param>[=<value>];... */
    PARSE_INT (p, " ", payload);
    if (payload != -1 && payload == pt) {
      gchar **pairs;
      gint i;

      /* <param>[=<value>] are separated with ';' */
      pairs = g_strsplit (p, ";", 0);
      for (i = 0; pairs[i]; i++) {
        gchar *valpos;
        const gchar *val, *key;
        gint j;
        const gchar *reserved_keys[] =
            { "media", "payload", "clock-rate", "encoding-name",
          "encoding-params"
        };

        /* the key may not have a '=', the value can have other '='s */
        valpos = strstr (pairs[i], "=");
        if (valpos) {
          /* we have a '=' and thus a value, remove the '=' with \0 */
          *valpos = '\0';
          /* value is everything between '=' and ';'. We split the pairs at ;
           * boundaries so we can take the remainder of the value. Some servers
           * put spaces around the value which we strip off here. Alternatively
           * we could strip those spaces in the depayloaders should these spaces
           * actually carry any meaning in the future. */
          val = g_strstrip (valpos + 1);
        } else {
          /* simple <param>;.. is translated into <param>=1;... */
          val = "1";
        }
        /* strip the key of spaces, convert key to lowercase but not the value. */
        key = g_strstrip (pairs[i]);

        /* skip keys from the fmtp, which we already use ourselves for the
         * caps. Some software is adding random things like clock-rate into
         * the fmtp, and we would otherwise here set a string-typed clock-rate
         * in the caps... and thus fail to create valid RTP caps
         */
        for (j = 0; j < G_N_ELEMENTS (reserved_keys); j++) {
          if (g_ascii_strcasecmp (reserved_keys[j], key) == 0) {
            key = "";
            break;
          }
        }

        if (strlen (key) > 1) {
          tmp = g_ascii_strdown (key, -1);
          gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
          g_free (tmp);
        }
      }
      g_strfreev (pairs);
    }
  }

  /* parse framesize: field */
  if ((framesize = gst_sdp_media_get_attribute_val (media, "framesize"))) {
    gchar *p;

    /* p is now of the format <payload> <width>-<height> */
    p = (gchar *) framesize;

    PARSE_INT (p, " ", payload);
    if (payload != -1 && payload == pt) {
      gst_structure_set (s, "a-framesize", G_TYPE_STRING, p, NULL);
    }
  }

  return caps;

  /* ERRORS */
no_rtpmap:
  {
    GST_ERROR ("rtpmap type not given for dynamic payload %d", pt);
    return NULL;
  }
no_rate:
  {
    GST_ERROR ("rate unknown for payload type %d", pt);
    return NULL;
  }
}
示例#8
0
/*
 *  Mapping of caps to and from SDP fields:
 *
 *   m=<media> <UDP port> RTP/AVP <payload> 
 *   a=rtpmap:<payload> <encoding_name>/<clock_rate>[/<encoding_params>]
 *   a=fmtp:<payload> <param>[=<value>];...
 */
static GstCaps *
gst_sdp_demux_media_to_caps (gint pt, const GstSDPMedia * media)
{
  GstCaps *caps;
  const gchar *rtpmap;
  const gchar *fmtp;
  gchar *name = NULL;
  gint rate = -1;
  gchar *params = NULL;
  gchar *tmp;
  GstStructure *s;
  gint payload = 0;
  gboolean ret;

  /* get and parse rtpmap */
  if ((rtpmap = gst_sdp_media_get_attribute_val (media, "rtpmap"))) {
    ret = gst_sdp_demux_parse_rtpmap (rtpmap, &payload, &name, &rate, &params);
    if (ret) {
      if (payload != pt) {
        /* we ignore the rtpmap if the payload type is different. */
        g_warning ("rtpmap of wrong payload type, ignoring");
        name = NULL;
        rate = -1;
        params = NULL;
      }
    } else {
      /* if we failed to parse the rtpmap for a dynamic payload type, we have an
       * error */
      if (pt >= 96)
        goto no_rtpmap;
      /* else we can ignore */
      g_warning ("error parsing rtpmap, ignoring");
    }
  } else {
    /* dynamic payloads need rtpmap or we fail */
    if (pt >= 96)
      goto no_rtpmap;
  }
  /* check if we have a rate, if not, we need to look up the rate from the
   * default rates based on the payload types. */
  if (rate == -1) {
    const GstRTPPayloadInfo *info;

    if (GST_RTP_PAYLOAD_IS_DYNAMIC (pt)) {
      /* dynamic types, use media and encoding_name */
      tmp = g_ascii_strdown (media->media, -1);
      info = gst_rtp_payload_info_for_name (tmp, name);
      g_free (tmp);
    } else {
      /* static types, use payload type */
      info = gst_rtp_payload_info_for_pt (pt);
    }

    if (info) {
      if ((rate = info->clock_rate) == 0)
        rate = -1;
    }
    /* we fail if we cannot find one */
    if (rate == -1)
      goto no_rate;
  }

  tmp = g_ascii_strdown (media->media, -1);
  caps = gst_caps_new_simple ("application/x-rtp",
      "media", G_TYPE_STRING, tmp, "payload", G_TYPE_INT, pt, NULL);
  g_free (tmp);
  s = gst_caps_get_structure (caps, 0);

  gst_structure_set (s, "clock-rate", G_TYPE_INT, rate, NULL);

  /* encoding name must be upper case */
  if (name != NULL) {
    tmp = g_ascii_strup (name, -1);
    gst_structure_set (s, "encoding-name", G_TYPE_STRING, tmp, NULL);
    g_free (tmp);
  }

  /* params must be lower case */
  if (params != NULL) {
    tmp = g_ascii_strdown (params, -1);
    gst_structure_set (s, "encoding-params", G_TYPE_STRING, tmp, NULL);
    g_free (tmp);
  }

  /* parse optional fmtp: field */
  if ((fmtp = gst_sdp_media_get_attribute_val (media, "fmtp"))) {
    gchar *p;
    gint payload = 0;

    p = (gchar *) fmtp;

    /* p is now of the format <payload> <param>[=<value>];... */
    PARSE_INT (p, " ", payload);
    if (payload != -1 && payload == pt) {
      gchar **pairs;
      gint i;

      /* <param>[=<value>] are separated with ';' */
      pairs = g_strsplit (p, ";", 0);
      for (i = 0; pairs[i]; i++) {
        gchar *valpos, *key;
        const gchar *val;

        /* the key may not have a '=', the value can have other '='s */
        valpos = strstr (pairs[i], "=");
        if (valpos) {
          /* we have a '=' and thus a value, remove the '=' with \0 */
          *valpos = '\0';
          /* value is everything between '=' and ';'. FIXME, strip? */
          val = g_strstrip (valpos + 1);
        } else {
          /* simple <param>;.. is translated into <param>=1;... */
          val = "1";
        }
        /* strip the key of spaces, convert key to lowercase but not the value. */
        key = g_strstrip (pairs[i]);
        if (strlen (key) > 1) {
          tmp = g_ascii_strdown (key, -1);
          gst_structure_set (s, tmp, G_TYPE_STRING, val, NULL);
          g_free (tmp);
        }
      }
      g_strfreev (pairs);
    }
  }
  return caps;

  /* ERRORS */
no_rtpmap:
  {
    g_warning ("rtpmap type not given for dynamic payload %d", pt);
    return NULL;
  }
no_rate:
  {
    g_warning ("rate unknown for payload type %d", pt);
    return NULL;
  }
}
示例#9
0
EvaProcessInfo *
eva_process_info_get (guint          pid,
                      GError       **error)
{
  EvaProcessInfo *rv;
  char *filename = g_strdup_printf ("/proc/%u/stat", pid);
  FILE *fp = fopen (filename, "r");
  char *content;
  const char *at;
  const char *end_name;
  gint dummy_int;
  guint dummy_uint;
  if (fp == NULL)
    {
      /* TODO: remap some error codes,
         like ENOENT to EVA_ERROR_PROCESS_NOT_FOUND. */
      g_set_error (error, EVA_G_ERROR_DOMAIN,
                   eva_error_code_from_errno (errno),
                   "error opening %s: %s",
                   filename, g_strerror (errno));
      g_free (filename);
      return NULL;
    }
  content = eva_stdio_readline (fp);
  if (content == NULL)
    {
      fclose (fp);
      g_set_error (error, EVA_G_ERROR_DOMAIN,
                   EVA_ERROR_NO_DOCUMENT,
                   "expected line of text in %s", filename);
      g_free (filename);
      return NULL;
    }
  fclose (fp);
  fp = NULL;

  rv = g_new (EvaProcessInfo, 1);
  rv->exe_filename = NULL;

  at = content;

#define ERROR_CLEANUP_AND_RETURN()                      \
  G_STMT_START{                                         \
    g_free (rv);                                        \
    g_free (rv->exe_filename);                          \
    g_free (filename);                                  \
    g_free (content);                                   \
    return NULL;                                        \
  }G_STMT_END
#define PARSE_ERROR(type)                               \
  G_STMT_START{                                         \
    g_set_error (error, EVA_G_ERROR_DOMAIN,             \
                 EVA_ERROR_END_OF_FILE,                 \
                 "error parsing %s from %s",            \
                 type, filename);                       \
    ERROR_CLEANUP_AND_RETURN();                         \
  }G_STMT_END
#define PARSE_ERROR_EXPECTED(what)                      \
  G_STMT_START{                                         \
    g_set_error (error, EVA_G_ERROR_DOMAIN,             \
                 EVA_ERROR_END_OF_FILE,                 \
                 "expected %s in %s",                   \
                 what, filename);                       \
    ERROR_CLEANUP_AND_RETURN();                         \
  }G_STMT_END
#define PARSE_INT(lvalue, base)                         \
  G_STMT_START{                                         \
    char *end;                                          \
    lvalue = strtol (at, &end, base);                   \
    if (at == end)                                      \
      PARSE_ERROR ("int");                              \
    at = end;                                           \
    EVA_SKIP_WHITESPACE (at);                           \
  }G_STMT_END
#define PARSE_UINT(lvalue, base)                        \
  G_STMT_START{                                         \
    char *end;                                          \
    lvalue = strtoul (at, &end, base);                  \
    if (at == end)                                      \
      PARSE_ERROR ("uint");                             \
    at = end;                                           \
    EVA_SKIP_WHITESPACE (at);                           \
  }G_STMT_END

  /* 'pid' */
  PARSE_INT (rv->process_id, 0);

  /* 'comm' */
  if (*at != LPAREN)
    PARSE_ERROR_EXPECTED ("left-paren");
  at++;
  end_name = strchr (at, RPAREN);
  if (end_name == NULL)
    PARSE_ERROR_EXPECTED ("right-paren");
  rv->exe_filename = g_strndup (at, end_name - at);
  at = end_name + 1;
  EVA_SKIP_WHITESPACE (at);

  /* 'state' */
  if (*at == 0 || strchr ("RSDZTW", *at) == NULL)
    PARSE_ERROR_EXPECTED ("a valid process state, one of R,S,D,Z,T,W");
  rv->state = *at;
  at++;
  EVA_SKIP_WHITESPACE (at);

  /* 'ppid' */
  PARSE_INT (rv->parent_process_id, 0);

  /* 'pgrp' */
  PARSE_INT (rv->process_group_id, 0);

  /* 'session' */
  PARSE_INT (rv->session_id, 0);

  /* 'tty_nr' */
  PARSE_INT (rv->tty_id, 0);

  /* 'tpgid' */
  PARSE_INT (dummy_int, 0);

  /* 'flags' */
  PARSE_INT (dummy_int, 8);

  /* 'minflt' */
  PARSE_UINT (dummy_uint, 0);
  /* 'cminflt' */
  PARSE_UINT (dummy_uint, 0);
  /* 'majflt' */
  PARSE_UINT (dummy_uint, 0);
  /* 'cmajflt' */
  PARSE_UINT (dummy_uint, 0);

  /* 'utime' */
  PARSE_UINT (rv->user_runtime, 0);
  /* 'stime' */
  PARSE_UINT (rv->kernel_runtime, 0);
  /* 'cutime' */
  PARSE_UINT (rv->child_user_runtime, 0);
  /* 'cstime' */
  PARSE_UINT (rv->child_kernel_runtime, 0);

  /* 'priority' */
  PARSE_INT (dummy_int, 0);

  /* 'nice' */
  PARSE_INT (rv->nice, 0);

  /* '0' */
  PARSE_INT (dummy_int, 0);

  /* 'itrealvalue */
  PARSE_INT (dummy_int, 0);
  /* 'starttime' */
  PARSE_INT (dummy_int, 0);

  /* 'vsize' */
  PARSE_UINT (rv->virtual_memory_size, 0);

  /* 'rss' */
  PARSE_UINT (dummy_uint, 0);
  rv->resident_memory_size = dummy_uint * get_page_size ();

  /* 'rlim' */
  PARSE_UINT (dummy_uint, 0);

  /* TODO: there's more fields, but we don't much care.
     also the info in 'statm' is worth checking out */

  g_free (filename);
  g_free (content);

  return rv;
}
示例#10
0
文件: lib.c 项目: CyberShadow/hax11
static void readConfig(const char* fn)
{
	//log_debug("Reading config from %s\n", fn);
	FILE* f = fopen(fn, "r");
	if (!f)
	{
		// Create empty file if it does not exist
		f = fopen(fn, "w");
		if (f) fclose(f);
		return;
	}

	while (!feof(f))
	{
		char buf[1024];
		if (!fgets(buf, sizeof(buf), f))
			break;
		if (buf[0] == '#')
			continue;
		//log_debug("Got line: %s'\n", buf);
		char *p = strchr(buf, '=');
		if (!p)
			continue;
		*p = 0;
		p++;
		//log_debug("Got line: '%s' = '%s'\n", buf, p);

		#define PARSE_INT(x)						\
			if (!strcasecmp(buf, #x))				\
				config.x = atoi(p);					\
			else

		PARSE_INT(mainX)
		PARSE_INT(mainY)
		PARSE_INT(mainW)
		PARSE_INT(mainH)
		PARSE_INT(desktopW)
		PARSE_INT(desktopH)
		PARSE_INT(actualX)
		PARSE_INT(actualY)
		PARSE_INT(enable)
		PARSE_INT(debug)
		PARSE_INT(joinMST)
		PARSE_INT(maskOtherMonitors)
		PARSE_INT(resizeWindows)
		PARSE_INT(resizeAll)
		PARSE_INT(moveWindows)
		PARSE_INT(fork)
			log_error("Unknown option: %s\n", buf);
	}
	fclose(f);
	//log_debug("Read config: %d %d %d %d\n", config.joinMST, config.maskOtherMonitors, config.resizeWindows, config.moveWindows);
}