sexta-feira, 2 de novembro de 2012

Funções com argumentos variáveis – va_list



Em alguns casos podemos desejar não ter um número de argumentos fixos em nossas funções, como é caso da função printf. Existe uma forma de implementar esse mecanismo, bem simples, como veremos a seguir:

va_list

Tipo usado para armazenar informações sobre nossa lista de argumentos. Ela está declarada em <stdarg.h>. Existem três macros nesse header responsáveis por manipular a lista, são elas:

void va_start(va_list ap, last);
Responsável por inicializar nossa lista "ap".

type va_arg(va_list ap, type);
Retorna o próximo argumento da lista

void va_end(va_list ap);
Cada va_start precisa ser finalizada com um va_end.

Para exemplificar, vamos fazer um função que recebe um número variável de argumentos do tipo int, e retorna a soma deles.  

#include <stdarg.h>
#include <stdio.h>

int IntSum(int TotalNumbers,...){
va_list numbers;
int TotalSum, counter;
va_start(numbers,TotalNumbers);
TotalSum = 0;
for(counter = 0; counter < TotalNumbers;counter++){
TotalSum+=va_arg(numbers,int);
}
va_end(numbers);
return TotalSum;
}

int main(){
printf("\n\t Soma Total 5 + 2 + 3 + 35 = %d",IntSum(4,5,2,3,35));
printf("\n\t Soma Total 10 + 2 = %d",IntSum(2,10,2));
return 1;
}

Após compilar e executar, o resultado deve ser:
Soma Total 5 + 2 + 3 + 35 = 45
Soma Total 10 + 2 = 12

Veja que o primeiro parâmetro indica quantos elementos estamos passando.



sábado, 2 de junho de 2012

multithreaded client em C

Olá pessoal, hoje vamos implementar um cliente multithread em C. Na verdade ele não faz muita coisa, mas o objetivo é gerar um número de conexões que irão ou não se desconectar do servidor, ou seja, pode funcionar como um programa teste para o seu servidor.

Para facilitar o nosso entendimento, vamos dividir nosso projeto em arquivos. Nosso arquivos serão os seguintes:
Para funções de entrada de dados e nossos tipos:
Data.c
Data.h

Para nossas funções de rede:
Functions.c
Functions.h

Nosso programa principal:
main.c

Nosso makefile
Makefile

===========================Data.h========================================

#ifndef DATA_H
#define DATA_H

typedef enum op
{
CONNECTTERMINATE = 1,
CONNECTWAIT
} Eoptions;

typedef struct configurations{
int numberOfConnections;
char * ip;
int port;
Eoptions opConf;
int socketId;
}config;

int splitOptions(char ** input, config * settings);
#endif

===========================Data.c========================================
#include<string.h>
#include<stdlib.h>
#include"Data.h"
#include<stdio.h>

int printSettings(config * settings){
if(settings == NULL){
return -1;
}
printf("\n\tNumber of Connections: %d", settings->numberOfConnections);
printf("\n\tIP of server: %s", settings->ip);
printf("\n\tPort of server: %d", settings->port);
printf("\n\tConnections options: %d",settings->opConf);
return 1;
}

int splitOptions(char ** input, config * settings){

if(input == NULL)
return 0;
if(settings == NULL)
return 0;
settings->numberOfConnections = atoi(input[1]);
settings->ip = input[2];
settings->port = atoi(input[3]);
settings->opConf = atoi(input[4]);
//Enable to debug
//printSettings(settings);

return 1;
}

===========================Functions.h=====================================

#ifndef FUNCTIONS_H
#define FUNCTIONS_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "Data.h"



void setLocal(struct sockaddr_in * clientAddr);
void setRemote(struct sockaddr_in * serverAddr, char* serverIp, int port);
void  mainClient(config* settings);


#endif

===========================Functions.c=====================================
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include "Functions.h"
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

int sendStr(config * settings, char * message){
char buffer[255];
if(settings == NULL)
return -1;
strcpy(buffer,message);
send(settings->socketId,buffer,strlen(buffer),0);

return 1;
}

void setLocal(struct sockaddr_in * clientAddr){
memset((void *) clientAddr, 0, sizeof(struct sockaddr_in));
if(clientAddr==NULL){
printf("\n\tClient struct is null in setLocal");
}
clientAddr->sin_family = AF_INET;
clientAddr->sin_addr.s_addr = INADDR_ANY;
clientAddr->sin_port = 0;

}

void setRemote(struct sockaddr_in * serverAddr, char* serverIp, int port){
serverAddr->sin_family = AF_INET;
serverAddr->sin_addr.s_addr = inet_addr(serverIp);
serverAddr->sin_port = htons(port);


}

void mainClient(config * settings){
struct sockaddr_in clientAddr, serverAddr;
int sock;
setLocal(&clientAddr); //local settings
setRemote(&serverAddr,settings->ip,settings->port);
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
settings->socketId = sock;
bind(sock, (const struct sockaddr *) &serverAddr, sizeof(serverAddr));
printf("\n\tConnecting");
if (connect(sock, (const struct sockaddr *) &serverAddr, sizeof(serverAddr)) != 0) {
perror("client");
return;
}

if(settings->opConf == CONNECTTERMINATE){
sendStr(settings, "I will close in ten seconds");

sleep(10);
close(sock);
}
else{
sendStr(settings, "I will never close");
while(1){

sleep(10);
}
}

}

===========================main.c=====================================
#include<stdio.h>
#include<pthread.h>
#include"Functions.h"
#include"Data.h"

int main(int argc, char** argv){
config settings;
int cont;
pthread_t thread[10];
if(argc != 5){
printf("\n\tInvalid options.");
printf("\n\tUsage: MultiThreadClient Ncon IP Port option");
printf("\n\tNcon - Number of connections");
printf("\n\tIP - Server IP");
printf("\n\tPort - Port server is listening");
printf("\n\tOption: \n\t1: connect and terminate\n\t2: connect and never terminate");
return 0;
}

if(splitOptions(argv, &settings) == 0)
return 0;
for(cont = 0; cont < settings.numberOfConnections; cont++){
printf("\n\tCreatingthread: %d", cont);
pthread_create( &thread[cont], NULL,(void*) &mainClient , (void *)&settings);
}
printf("\n\tPress a key");
getchar();
return 1;
}

===========================Makefile=====================================
CC=gcc
CFLAGS=-W -Wall -g
SRC= $(wildcard *.c)
OBJ= $(SRC:.c=.o)

all: clean $(OBJ)
gcc -o MultiThreadClient $(OBJ) -lpthread
%.o: %.c
@$(CC) -o $@ -c $< $(CFLAGS)
clean:
rm -f *.o 

Para executar:
./MultiThreadClient 8 127.0.0.1 4001 1

ou
./MultiThreadClient 8 127.0.0.1 4001 2

Estamos assumindo que você possui um server em seu pc local escutando na porta 4001. 





domingo, 25 de março de 2012

Usando o GDB

olá pessoal, hoje vamos tentar usar o GDB, uma ferramenta de debug muito útil no desenvolvimento.
Para demostrar o seu uso, vamos fazer um pequeno programa e uma função para somar todos os elementos de uma matriz.

Nosso código fonte:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main()
  4. {
  5. int matriz[5] = {1,2,5,3};
  6. int * result;
  7. somaArray(matriz,5,result);
  8. printf("\n\tResult: %d\n", *result);
  9. return 1;
  10. }
  11. void somaArray(int * m, int size, int * result)
  12. {
  13. int acul=0;
  14. int x;
  15. for(x=0;x<size;x++)
  16. {
  17. acul+=m[x];
  18. }
  19. *result = acul;
  20. }

Bom, obviamente esse código tem um erro, não inicializamos o ponteiro “result” assim quando formos escrever nele vamos receber um “segmentation fault”

Bom mas por enquanto vamos fazer de conta que não tem problema e vamos compilar nosso código fonte com o seguinte comando:

eduardo@debian:~/Documents/programas/gdb$ gcc -o gdbTest gdbTest.c

Vamos executar:

eduardo@debian:~/Documents/programas/gdb$ ./gdbTest

E vamos ver o resultado da soma!!!!!
Segmentation fault

É, ainda não foi dessa vez. Bom, vamos pedir ajuda ao gdb. Aqui eu já estou com o gdb instalado. Se você não tem ele instalado você pode rodar o comando:
apt-get install gdb

Mas não vamos cobrir aqui detalhes da instalação. Nosso objetivo, agora, é usar.

Primeiro passo: vamos compilar nosso programa novamente, com a opção “-g”, assim, o gdb teerá melhores condições de analisar nosso código e nos permitirá, por exemplo imprimir a linha do SIGSEGV.

Vamos lá:
eduardo@debian:~/Documents/programas/gdb$ gcc -g -o gdbTest gdbTest.c

Segundo passo: rodar com o gdb

eduardo@debian:~/Documents/programas/gdb$ gdb ./gdbTest
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/eduardo/Documents/programas/gdb/gdbTest...done.
(gdb)

Vai ficar piscando o cursor. Apenas digitamos: run

(gdb) run
Starting program: /home/eduardo/Documents/programas/gdb/gdbTest

Program received signal SIGSEGV, Segmentation fault.
0x000000000040059e in somaArray (m=0x7fffffffe280, size=5, result=0x0)
at gdbTest.c:23
23 *result = acul;
(gdb)

Já recebemos o nosso “SIGSEGV”, bom veja que ele já nos informou a linha que isso ocorreu – linha 23 – e em qual função – somaArray – veja que ele nos passou os argumentos da função, veja que o primeiro parâmetro é um ponteiro, e realmente tem um endereço ali. O segundo parâmetro é um int, e realmente tem o valor 5 que estamos passando, mas e o ponteiro result? Veja que ele é nulo, e na linha 23 estamos tentando escrever nele!!! então achamos a causa do sigsegv.

Se você desejar ver quem chamou a função somaArray, você pode digital o comando bt (backtrace). Imagine que você poderia ter no main várias chamadas para essa função no main. Veja que é na linha 23 que o problema acontece, mas essa linha está certa, o problema é que já estamos recebendo o ponteiro nulo!!!

então:
(gdb) bt
#0 0x000000000040059e in somaArray (m=0x7fffffffe280, size=5, result=0x0)
at gdbTest.c:23
#1 0x0000000000400539 in main () at gdbTest.c:7
(gdb)

Agora vamos no main na linha 7 e ver como é a chamada.
somaArray(matriz,5,result);

bom a variável matriz está inicializada corretamente. O parâmetro size já é 5, então está certo. O result, logo acima, na linha 6 foi declarado:
int * result;

Mas não foi inicializado!!! e logo abaixo já o usamos.

Bom, achamos o problema, agora corrigimos:

Vamos alterar a linha 6 para:
int * result = (int *) malloc(sizeof(int));

vamos compilar novamente, pode ser sem a flag “-g”.

eduardo@debian:~/Documents/programas/gdb$ gcc -o gdbTest gdbTest.c

uardo@debian:~/Documents/programas/gdb$ ./gdbTest

Result: 11
eduardo@debian:~/Documents/programas/gdb$

Resolvido!!!!

sábado, 25 de fevereiro de 2012

Comunicação entre processos – PIPES em C++

Sistema operacional utilizado: Linux debian 2.6.32-5-amd64

O nosso caso de exemplo será com o que chamamos de "Named PIPES" também conhecidas como FIFO. Onde a utilizaremos para comunicação entre dois processos. Basicamente, teremos um servidor, que criará uma PIPE/FIFO onde o cliente escreverá um comando. O Servidor lerá essa mensagem, e escreverá na PIPE/FIFO do cliente a resposta do comando. Ou seja, o cliente também terá que criar sua PIPE para que o servidor possa escrever. Então, utilizaremos duas PIPES, uma criada pelo Server e a outra criada pelo cliente.

A Pipe criada pelo servidor deve ser conhecida antes de rodarmos o cliente. O cliente irá criar sua Pipe pelo PID do processo e irá transmitir o nome da Pipe para o servidor através da PIPE do server, assim o servidor será capaz de abrir a Pipe do cliente e escrever o resultado nela.

No nosso exemplo vamos usar três arquivos de codificação.

Main.cpp – Esse arquivo conterá a codificação para o nosso servidor
client.cpp – Esse arquivo conterá a codificação para o nosso cliente
header_fifo.h – Esse arquivo conterá a implementação de nossa classe.

Teremos tamém um quarto arquivo, um Makefile, que será responsável por compilar nosso exemplo.

Teremos duas opções de compilação, uma para Debug, onde você poderá ver as mensagens sendo trocadas entre cliente e servidor, o que será útil para modificar ou entender melhor o processo de comunicação.

Então vamos ao código de nosso servidor:


#include "fifo_header.h"

int main(int argc, char *argv[])
{
    int processID;
    if(argv[1]!=NULL)
    {
        if(strcmp("stop",argv[1]) ==0)
        {
                system("killall PipeServer");
                return 1;
        }
        else
        {
            if(strcmp("Debug",argv[1]) !=0)
            {

                processID = fork();

                if (processID < 0)
                {
                    exit(EXIT_FAILURE);
                }
                else
                {


                    if (processID > 0)
                    {
                        exit(EXIT_SUCCESS);
                    }
                }
            }
        }
    }

    FIFO myFifo, clientFifo;
    int PipeToWrite, PipeToRead, returnedValue, ready = 0;
    data buffer;
    myFifo.setName(argv[1]);
    myFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));
#ifdef DEBUG
    cout << "Name of FIFO: " << myFifo.getName() << endl;
    cout << "Path of FIFO: " <<myFifo.getPath() << endl;
    cout.flush();
#endif

    /*
    Vamos iniciar a criação de nossa PIPE
    */
    mknod(myFifo.getName(), S_IFIFO | 0666, 0);

    /*
    Vamos abrir a nossa PIPE que usaremos para ler do cliente
    */

    returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);
    if(returnedValue == -1)
    {
        printf("\n\tError in open PIPE\n");
        perror(myFifo.getMessage());
        return 1;
    }
    myFifo.pipeDescriptor = returnedValue;

    while (true)
    {

        returnedValue = myFifo.readFromPipe(clientFifo);

        if(returnedValue > 0)
        {
#ifdef DEBUG
        cout <<"retuned value from read: "<<returnedValue <<endl;
        cout <<"What Client said is: " <<myFifo.getMessage()<<endl;
#endif
        ready=0;
        do
        {
            returnedValue = open(clientFifo.getName(), O_WRONLY/*|O_NDELAY*/);
                if(returnedValue == -1)
                {
                    perror(clientFifo.getName());
                    return 1;
                }
#ifdef DEBUG
            cout <<"Pipe client Name: "<< clientFifo.getName() <<endl;
#endif
            clientFifo.pipeDescriptor = returnedValue;
            myFifo.writeToPipe(clientFifo);
            ready=1;


            }while(!ready);
            clientFifo.closePipe();
        }
    }

    return 1;
}

Agora o código de nosso cliente

#include "fifo_header.h"

int main(int argc, char *argv[])
{
    /*
    argv deve ser o mesmo do servidor.
    Assim termos como informar o nome da PIPE e o local onde o servidor
    a criou
    */
    FIFO myFifo, serverFifo;
    data buffer;
    int numberOfBytes, returnedValue, readOK;
    serverFifo.setName(argv[1]);
    serverFifo.setPath(strcat(strcat(argv[2],"/"),argv[1]));

#ifdef DEBUG
    cout << "Name of FIFO: " << myFifo.getName() << endl;
    cout << "Path of FIFO: " <<myFifo.getPath() << endl;
    cout.flush();
#endif
    sprintf(buffer.name, "/home/eduardo/Documents/programas/FIFO/%d", getpid( ));
    myFifo.setName(buffer.name);

    if (mknod(myFifo.getName(), S_IFIFO | 0666, 0) < 0)
    {
        perror(myFifo.getName());
        return 1;
    }
    if ((returnedValue = open(serverFifo.getName(), O_WRONLY)) == -1)
    {
        perror(serverFifo.getName());
        return 1;
    }
    serverFifo.pipeDescriptor = returnedValue;
    while(true)
    {

        cout << "\ncmd" <<endl;
#ifdef DEBUG
        cout <<"PIPE of server: " << serverFifo.getName() <<endl;
#endif
        numberOfBytes = read(fileno(stdin), buffer.message, PIPE_BUF);
#ifdef DEBUG
        cout <<"what you write: "<<buffer.message<<endl;
#endif
        if (!strncmp("exit", buffer.message, numberOfBytes - 1))
            break;

        write(serverFifo.pipeDescriptor,(char *) &buffer, sizeof(buffer));

        returnedValue = open(myFifo.getName(), O_RDONLY | O_NDELAY);

        if(returnedValue == -1)
        {
            perror(myFifo.getName());
            return 1;
        }
        myFifo.pipeDescriptor = returnedValue;
        readOK = 0;
        while (readOK != 1)
        {
            if((numberOfBytes = read(myFifo.pipeDescriptor, buffer.message, PIPE_BUF)) > 0)
            {
                write(fileno(stdout), buffer.message, numberOfBytes);
                memset(buffer.message,0x0,PIPE_BUF);
                readOK=1;
            }


        }
        myFifo.closePipe();


    }

    serverFifo.closePipe();
    return 1;
}


Agora a implementação de nossa classe


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <iostream>
#include <cstdio>
#include <fstream>
#include <errno.h>
using namespace std;

typedef struct dataSet
{
        char name[255];
        char path[255];
        char message[PIPE_BUF];
}data;

class FIFO
{
    private:
        data info;
    public:
        int pipeDescriptor;
        char* getName();
        void setName(char*);
        char* getPath();
        void setPath(char*);
        char* getMessage();
        void writeMessage(char*);
        void closePipe();
        int writeToPipe(FIFO&);
        int readFromPipe(FIFO&);

};

char* FIFO::getName()
{
    return info.name;
}

void FIFO::setName(char * nameToSet)
{
    strcpy(info.name,nameToSet);
}

char* FIFO::getPath()
{
    return info.path;
}

void FIFO::setPath(char *pathToSet)
{
    strcpy(info.path, pathToSet);
}


char* FIFO::getMessage()
{
    return info.message;
}

void FIFO::writeMessage(char *messageToWrite)
{
   strcpy(info.message, messageToWrite);
}

int FIFO::writeToPipe(FIFO& pipeToWrite)
{
    FILE * result;
    int readFromFile, numberOfBytes, popenIsOk;
    char buffer[PIPE_BUF], stringBuff[PIPE_BUF];
#ifdef DEBUG
    cout <<"what will be the command to sheel: " << getMessage()<<endl;
#endif
    result = popen(getMessage(), "r");
    if(!result)
    {
#ifdef DEBUG
        cout <<"result is null" << endl;
        perror("errot in open file");
#endif
        strcpy(stringBuff,"Command not found");
        write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
        return -1;
    }


    popenIsOk=0;
    while(popenIsOk!=1)
    {
        if ((numberOfBytes = read(fileno(result), buffer, PIPE_BUF))>0)
        {
            strcpy(stringBuff,buffer);
            write(pipeToWrite.pipeDescriptor,stringBuff,strlen(stringBuff)+1);
            memset(buffer, 0x0, PIPE_BUF);
#ifdef DEBUG
            cout <<"what will be written to client: " << stringBuff << endl;
#endif
            popenIsOk=1;

        }
    }

    pclose(result);

}

int FIFO::readFromPipe(FIFO& pipe)
{

    int returnedValue;
    data buffer;
    returnedValue = read(pipeDescriptor, (char *) &buffer, sizeof(buffer));
    if(returnedValue <=0 )
        return returnedValue;
    else
    {
        writeMessage(buffer.message);
        pipe.setName(buffer.name);
    }
    return returnedValue;
}

void FIFO::closePipe()
{
    close(pipeDescriptor);
}

Agora vamos ao nosso Makefile


CC:=g++

all:PipeServer Client

PipeServer:main.cpp fifo_header.h
$(CC) -o PipeServer main.cpp

Client:client.cpp fifo_header.h
$(CC) -o Client client.cpp

Debug: main.cpp client.cpp fifo_header.h
$(CC) -D DEBUG -o PipeServer main.cpp
$(CC) -D DEBUG -o Client client.cpp

Comandos necessários para compilar e rodar:
Para compilar os dois programas em modo normal: 
Comando: make all

Para executar:
Comando para Rodar o Server: ./PipeServer FIFO /home/eduardo/Documents/programas/FIFO
Onde: 
  1. FIFO é o Nome da PIPE a ser Criada
  2.  /home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela vai ser criada
Comando para Rodar o cliente: ./client FIFO /home/eduardo/Documents/programas/FIFO
  1. FIFO é o Nome da PIPE criada pelo server
  2.  /home/eduardo/Documents/programas/FIFO: Caminho da Pasta onde ela foi  criada
Como o Server roda em BackGround, se você desejar para o server dê o comando: ./PipeServer Stop
Para parar o cliente apenas digite exit

Para o dois programas em modo de DEBUG

comando para compilar: make Debug

Para rodar o server(agora ele não roda em BackGround Ctrl+C se desejar parar o server): ./PipeServer Debug /home/eduardo/Documents/programas/FIFO

Para rodar o cliente: ./client  Debug  /home/eduardo/Documents/programas/FIFO

Bons Testes!!!!


terça-feira, 31 de janeiro de 2012

Definindo Macros para diretivas de compilação

Hoje vamos aprender a usar algumas diretivas de compilação. As vezes é necessário fazermos um debug de nosso programa. Para isso ao invés de definirmo um #define em  nosso código, vamos incluir no momento da compilação.

Vamos ver um pequeno exemplo. Vamos criar um arquivo em c com o seguinte código:


#include <stdio.h>
#include <stdlib.h>

int main()
{
    int num1, num2, result;
    printf("\n\tDigite o priemiro Numero: ");
    scanf("%d",&num1);
    printf("\n\tDigite o segundo Numero: ");
    scanf("%d", &num2);

    #ifdef DEBUG
    printf("\n\tOs valores que voce digitou: %d %d", num1, num2);
    #endif

    printf("\n\tResultado %d + %d = %d", num1, num2, num1 + num2);

    return 1;
}

Agora, vamos compilar o nosso código com a seguinte sintaxe: gcc -D DEBUG -o somaDoisN soma.c
Quando o programa for executado, ele vai mostrar na tela o printf entre o #ifdef e o #endif

Vamos mudar a compilação, vamos retirar a diretiva de compilação para DEBUG:
gcc  -o somaDoisN soma.c 

Agora a mensagem não vai mais ser mostrada!!!!


sábado, 7 de janeiro de 2012

Shared Libraries no Linux

Shared libraries são bibliotecas que são carregadas pelos programas quando eles iniciam.

Precisamos entender a diferença entre o nome da biblioteca, soname, e seu real name.
Toda shared library tem um nome especial chamado de "soname". O soname tem o prefixo "lib", o nome da biblioteca, o sufixo ".so" seguido de ponto e um número de versão que é incrementado sempre que a interface muda. Normalmenteo soname é um link simbólico para o real name da shared library.
Exemplo de soname: libctest.so.1

Toda a shareed libray possui um real name. Que é o nome do arquivo que contém o código da biblioteca. O real name adiciona ao soname um ponto, um número, que é o minor number, outro ponto e o número da versão.
Exemplo de real name: libctest.so.1.0

Além disso, há um nome usado pelo compilador para requisitar a library, que é simplesmente o soname sem nenhum número de versão.
Exemplo: libctest.so


Colocando em prática:

Primeiramente vamos ter dois arquivos que são:
operacoes.c – que vai conter nosso código fonte
operacoes.h – que vai conter apenas as definições

Código do arquivo operacoes.c

#include "operacoes.h"

int somaDeInterios (int n1, int n2)
{
return n1 + n2;
}

int multInteiros (int n1, int n2)
{
return n1 * n2;
}

Código do arquivo operacoes.h

#ifndef _OPERACOES
#define _OPERACOES

int somaDeInterios (int n1, int n2);
int multInteiros (int n1, int n2);

#endif


Agora vamos digitar o comandos:
gcc -fPIC -c operacoes.c
Veja que fio gerado o aquivo: operacoes.o

Agora, vamos gerar a nossa library propriamente dita:

gcc -shared -Wl,-soname,libcmatematica.so.1 -o libcmatematica.so.1.0 operacoes.o

Vamos copiar o arquivo gerado: libcmatematica.so.1.0 para dentro da pasta /usr/lib, talvez você deva estar logado como root para executar esse passo:

cp libcmatematica.so.1.0 /usr/lib

Agora vamos criar os links necessários, talvez tenha que estar logado com root:

ln -sf /usr/lib/libcmatematica.so.1.0 /usr/lib/libcmatematica.so
ln -sf /usr/lib/libcmatematica.so.1.0 /usr/lib/libcmatematica.so.1

Bom, agora, vamos criar um simples programa para verificar se a nossa biblioteca realmente funciona.

Vamos chamar nosso arquivo de main.c

Código do arquivo main.c

#include "operacoes.h"
#include <stdio.h>

int main()
{
printf("\n\tTeste da Shared library");
printf("\n\t5 + 2 = %d", somaDeInterios(5,2));
printf("\n\t5 * 2 = %d", multInteiros(5,2));
}

Para compilar:
gcc main.c -lcmatematica -o operacoesInteiras

Resultado:
Teste da Shared library
5 + 2 = 7
5 * 2 = 10

domingo, 18 de dezembro de 2011

Shell Script - Criando Script para trocar a extensão de vários arquivos

Olá pessoal,

hoje vamos falar um pouco sobre shell script. O objetivo é criar um script, muito simples, que seja capaz de alterar a extensão de nossos arquivos.

Para testar, vamos criar em nosso diretório de testes os seguintes arquivos:


ChangeExtension.sh  -> nosso script
First.txt -> arquivo para testes
Second.txt -> arquivo para testes

Bom, agora, vamos ao nosso script, que pega o nome os arquivos, e troca a sua extensão de acordo com os parâmetros passados a eles:


NUMBER_OF_ARGUMENTS=$#
ORIGINAL_EXTENSION=$1
NEW_EXTENSION=$2


if [ $NUMBER_OF_ARGUMENTS -ne 2 ]
then
    echo "you need to pass two extensions like: ChangeExtension .txt .teste"
    exit 1
fi

echo "Original Extension: "  $ORIGINAL_EXTENSION
echo "New Extension: " $NEW_EXTENSION




for file in $(ls | awk -F"." '{print $1}')
do
    if [ -e $file$ORIGINAL_EXTENSION ]
    then

        mv -v $file$ORIGINAL_EXTENSION $file$NEW_EXTENSION
    fi

done

O que é feito é basicamente o seguinte:
1° - pegamos os argumentos passados passados pela linha de comando: $1 é o primeiro $2 é o segundo. $# é o número de argumentos.
2º - Testamos se o número de argumentos é válido
3° - Para cada arquivo com a extensão informada, a substituímos pela nova extensão.

Vamos a um exemplo:
Vamos renomear nossos arquivos de .txt para .old.txt

Apenas digitamos o comando:  ./ChangeExtension.sh .txt .old.txt
Resultado:
ChangeExtension.sh  -> nosso script, inalterado
First.old.txt  Second.old.txt -> dois arquivos com a extensão alterada.

Bom, por hoje é isso