Olá...
Recentemente tive uma demanda de um cliente, com uma banda de acesso muito pequena. Por tanto tive que gerar um executável muito pequeno para enviar para ele.
A principio utilizei as bibliotecas do Qt para ler um Xml e fazer as modificações. Por causa da lib do qt exigir dependências externas, e não achei como remover as dependências, meu executável ficou com quase 40Mb.
Isso mesmo! 40 Mb :-/ Imagina minha decepção.
Partir para a libxml2, biblioteca bem documentada (não conhecia mas me agradou muito). Só que o executável e as bibliotecas juntas geram um montante de 10Mb.
Sei o que vocês vão perguntar? Não poderia gerar executável estático, gerar uma nova lib com só o necessário, certificar que os usuário já tenham as principais bibliotecas na máquina.
Respondendo: Era tarde da noite e não tinha como ter essas "regalias". Tinha que gerar um executável para a manhã seguinte.
Eis minha solução... gerei um executável em C com as bibliotecas básicas: stdio.h, string.h e stdlib.h
O executável ficou minusculo... 100 kb. Isso mesmo! só que deu um trabalho danado para re-escrever algumas funções triviais das bibliotecas Qt e libxml2. E outra coisa, eu tratei o arquivo xml como um array de bytes ;-)
- Achei o texto relacionado a tag
- Alterei o texto
- Salvei em um novo arquivo
Após esta história, vem o motivo deste post... foi difícil achar na net implementações das funções em C, então segue meu código abaixo para quem quiser reaproveitar:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
FILE *pFile_old = NULL,
*pFile_new = NULL;
static char strTag_new[4096] = {};
int size_new = 0;
static char strTag_old[4096] = {};
int size_old = 0;
static char xmlData[4096] = {};
int size_xml = 0;
static char text[] = "Texto\\\0";
int size_text;
/**
* @brief getPositionBegin
* @param source
* @param substr
* @return position where start substr in source
*/
int getPositionBegin(char *source, char *substr)
{
char *tmp = strstr(source, substr);
int pos = (tmp - source);
return pos;
}
/**
* @brief getPositionEnd
* @param source
* @param substr
* @return position where end substr in source
*/
int getPositionEnd(char *source, char *substr)
{
char *tmp = strstr(source, substr);
int pos = (tmp - source) + strlen(substr);
return pos;
}
/**
* @brief readFileBytes
* @param name
*/
void readFileBytes(const char *name)
{
pFile_old = fopen(name , "rb");
if (pFile_old == NULL) {
fputs ("File error",stderr);
exit(1);
}
// obtain file size:
fseek (pFile_old , 0 , SEEK_END);
size_xml = ftell (pFile_old);
rewind (pFile_old);
// copy the file into the buffer:
size_t result = fread (xmlData, 1, size_xml, pFile_old);
if (result != size_xml) {
fputs ("Reading error",stderr);
exit(2);
}
xmlData[size_xml+1] = '\0';
fclose(pFile_old);
pFile_old = NULL;
}
/**
* @brief writeFileData
* @param name
* @param data
* @return
*/
int writeFileData (char *name, char *data)
{
pFile_new = fopen(name, "wb");
size_t result = fwrite (data , sizeof(char), strlen(data), pFile_new);
// if (result > 0)
// exit(3);
fclose (pFile_new);
pFile_new = NULL;
return result;
}
char *substring(char *string, int position, int length)
{
char *pointer;
int c;
pointer = malloc(length+1);
if( pointer == NULL )
exit(EXIT_FAILURE);
for( c = 0 ; c < length ; c++ )
*(pointer+c) = *((string+position-1)+c);
*(pointer+c) = '\0';
return pointer;
}
/**
* @brief Insert substring into source
* @param source
* @param substr
* @param position
* @warning array start in 1
*/
void insert_substring(char *source, char *substr, int position)
{
char *f, *e;
int length;
length = strlen(source);
// get text before 'substr'
f = substring(source, 1, position - 1 );
// get text after 'substr'
e = substring(source, position, (length-position)+1);
strcpy(source, "");
strcat(source, f);
free(f);
strcat(source, substr);
strcat(source, e);
free(e);
}
/**
* @brief remove_substring
* @param source
* @param substr
*/
void remove_substring(char *source, char *substr)
{
int begin = getPositionBegin(source, substr);
int end = getPositionEnd(source, substr);
while( source = strstr(source, substr) )
memmove(source,source+strlen(substr), 1+strlen(source+strlen(substr)));
}
void *replace_str(char *source, char *_old, char *_new)
{
int pos = getPositionBegin(source, _old);
remove_substring(source, _old);
insert_substring(source, _new, pos);
}
Segue a função main:
int main(int argc, char* argv[])
{
// reading xml file
readFileBytes("c:\\exemplo.xml");
// find position of tag
int begin = 15 + getPositionBegin(xmlData, "<ReceiptFooter>");
// find position of tag
int end = getPositionBegin(xmlData, "</ReceiptFooter>");
// define size of text
size_old = (end-begin);
int tam = (end-begin) - 1;
// check text empty
if (tam > 0) {
// get the text
strncpy(strTag_old, (xmlData+begin), size_old);
strTag_old[size_old + 1] = '\0';
//remove old tag text from xml
remove_substring(xmlData, strTag_old);
// Text split - remove lines that start with '--'
char *pch = strtok (strTag_old,"\\");
while (pch != NULL)
{
// check if string start with '--'
int pos = getPositionBegin(pch, "--");
if (pos != 0){
// put in new string
strcat(strTag_new, pch);
size_new += strlen(pch);
strcat(strTag_new, "\\");
size_new++;
}
pch = strtok (NULL, "\\");
}
strTag_new[size_new+1] = '\0';
}
// find the text
int pos = getPositionBegin(strTag_new, text);
if (pos < 0) {
//add text
insert_substring(strTag_new, text, 1);
}
// remove old tag text and put new in xml
insert_substring(xmlData, strTag_new, begin+1);
writeFileData("c:\\exemplo2.xml", xmlData);
// delete temporary files
system("del c:\\exemplo.xml");
system("del c:\\exemplo2.xml");
return(0);
}
Utilizando esse código, com includes das bibliotecas básicas do C. Pudi criar um executável sem parâmetros para o GCC.
GCC main.c
Abraços