summaryrefslogtreecommitdiff
path: root/lib/Handle.cc
blob: 7ad7cc6ec8d9c74c93a2401a802b35b853850825 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "Handle.h"
#include "config.h"

Handle::Handle(const Handle & nh) : h(nh.h >= 0 ? dup(nh.h) : nh.h), closed(false), nonblock(false) { }

Handle::~Handle() {
    if (h >= 0) {
	::close(h);
    }
}

Handle::Handle(int nh) : h(nh), closed(false) { }

int Handle::GetHandle() {
    return h;
}

ssize_t Handle::write(const void *buf, size_t count) throw (GeneralException) {
    ssize_t r, tr = 0;
    bool done, full = false;

    do {
	done = true;
	errno = 0;
        if ((r = ::write(h, buf, count)) < 0) {
    	    if ((!errno) || (errno = EAGAIN)) {
		// Avant de déclarer une erreur, on vérifie si ce n'est pas un
		// problème lié au fait qu'il n'y a plus de place libre. Cela peut
		// arriver si l'on agit sur un pipe ou un handle. Nous
		// attendons encore une fois avant de déclarer l'erreur,
		// grace au drapeau full.
		if (full) {
		    throw IOException(GetName(), IO_WRITE, count);
		} else {
		    done = false;
		    full = true;
		    if (nonblock) {
			throw IOAgain();
		    } else {
			sleep(1);
		    }
		}
	    } else {
		throw IOException(GetName(), IO_WRITE, count);
	    }
	} else if (r != count) {
	    full = done = false;
	    ((char *)buf) += r;
	    tr += r;
	}
    } while (!done);

    return r + tr;
}

ssize_t Handle::read(void *buf, size_t count) throw (GeneralException) {
    ssize_t r;

    errno = 0;
    if ((r = ::read(h, buf, count)) < 0) {
	if ((!errno) || (errno = EAGAIN)) {
	    // Avant de déclarer une erreur, on vérifie si ce n'est pas un
	    // problème lié au fait qu'il n'y a plus d'octets.
	    throw IOAgain();
	} else {
	    throw IOException(GetName(), IO_READ, count);
	}
    }
    
    if (!r) {
	::close(h);
	closed = true;
    }

    return r;
}

bool Handle::IsClosed(void) {
    return closed;
}

bool Handle::IsNonBlock(void) {
    return nonblock;
}

void Handle::SetNonBlock(void) {
    if ((h >= 0) || !nonblock) {
	fcntl(h, F_SETFL, O_NONBLOCK);
    }
    nonblock = true;
}

Handle & operator<<(Handle & h, const String & s) {
    char * p;
    
    p = s.to_charp();
    h.write(p, strlen(p));
    
    return h;
}

Handle & operator>>(Handle & h, String & s) {
    char t[BUFSIZ];
    int i = 0, r;
    
    while ((r = h.read(&(t[i]), 1)) && (i != (BUFSIZ - 1))) {
	// Il y a souvent des \r\n dans les sockets par exemple,
	// ou bien en lisant des fichiers au format MS-DOS. On
	// ignore le \r pour ne garder que le \n, standard sous Unix.
	if (t[i] == '\r') {
	    continue;
	}
	if (t[i] == '\n') {
	    break;
	} else {
	    i++;
	}
    }
    
    t[i] = '\0';
    s.set("%s", t);
    return h;
}

void Handle::close() {
    if (IsClosed()) {
	return;
    }
    
    if (h >= 0) {
	::close(h);
    }
    
    closed = 1;
}

bool Handle::CanRead(void) {
    return false;
}

bool Handle::CanWrite(void) {
    return false;
}

String Handle::GetName(void) {
    return _("Bare Handle - should not happend");
}