Coverage Report

Created: 2026-06-30 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/ofstd/libsrc/ofconsol.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 2000-2025, OFFIS e.V.
4
 *  All rights reserved.  See COPYRIGHT file for details.
5
 *
6
 *  This software and supporting documentation were developed by
7
 *
8
 *    OFFIS e.V.
9
 *    R&D Division Health
10
 *    Escherweg 2
11
 *    D-26121 Oldenburg, Germany
12
 *
13
 *
14
 *  Module:  ofstd
15
 *
16
 *  Author:  Marco Eichelberg
17
 *
18
 *  Purpose: Define alias for cout, cerr and clog
19
 *
20
 */
21
22
#include "dcmtk/config/osconfig.h"
23
#include "dcmtk/ofstd/ofconsol.h"
24
#include "dcmtk/ofstd/ofthread.h"
25
26
#include <cassert>
27
#ifdef HAVE_UNISTD_H
28
BEGIN_EXTERN_C
29
#include <unistd.h>
30
END_EXTERN_C
31
#endif
32
33
34
BEGIN_EXTERN_C
35
#include <fcntl.h>
36
#ifdef HAVE_IO_H
37
#include <io.h>
38
#endif
39
END_EXTERN_C
40
41
42
#ifdef DCMTK_GUI
43
  OFOStringStream COUT;
44
  OFOStringStream CERR;
45
#endif
46
47
OFConsole::OFConsole()
48
#ifdef DCMTK_GUI
49
: currentCout(&COUT)
50
, currentCerr(&CERR)
51
#else
52
5
: currentCout(&STD_NAMESPACE cout)
53
5
, currentCerr(&STD_NAMESPACE cerr)
54
#endif
55
5
, joined(0)
56
#ifdef WITH_THREADS
57
5
, coutMutex()
58
5
, cerrMutex()
59
#endif
60
5
{
61
5
}
62
63
STD_NAMESPACE ostream *OFConsole::setCout(STD_NAMESPACE ostream *newCout)
64
0
{
65
0
  lockCout();
66
0
  STD_NAMESPACE ostream *tmpCout = currentCout;
67
#ifdef DCMTK_GUI
68
  if (newCout) currentCout = newCout; else currentCout = &COUT;
69
#else
70
0
  if (newCout) currentCout = newCout; else currentCout = &STD_NAMESPACE cout;
71
0
#endif
72
0
  unlockCout();
73
0
  return tmpCout;
74
0
}
75
76
STD_NAMESPACE ostream *OFConsole::setCerr(STD_NAMESPACE ostream *newCerr)
77
0
{
78
0
  lockCerr();
79
0
  STD_NAMESPACE ostream *tmpCerr = currentCerr;
80
#ifdef DCMTK_GUI
81
  if (newCerr) currentCerr = newCerr; else currentCerr = &CERR;
82
#else
83
0
  if (newCerr) currentCerr = newCerr; else currentCerr = &STD_NAMESPACE cerr;
84
0
#endif
85
0
  unlockCerr();
86
0
  return tmpCerr;
87
0
}
88
89
void OFConsole::join()
90
0
{
91
0
  lockCerr();
92
0
  if (!joined)
93
0
  {
94
    // changing the state of "joined" requires that both mutexes are locked.
95
    // Mutexes must always be locked in the same order to avoid deadlocks.
96
0
    lockCout();
97
0
    joined = 1;
98
0
  }
99
100
  // now status is joined, so unlockCerr implicitly unlocks both mutexes
101
0
  unlockCerr();
102
0
  return;
103
0
}
104
105
void OFConsole::split()
106
0
{
107
0
  lockCerr();
108
0
  if (joined)
109
0
  {
110
    // since status is joined, lockCerr() has locked both mutexes
111
0
    joined = 0;
112
113
    // now status is unjoined, we have to unlock both mutexes manually
114
0
    unlockCout();
115
0
  }
116
0
  unlockCerr();
117
0
  return;
118
0
}
119
120
OFBool OFConsole::isJoined()
121
0
{
122
0
  lockCerr();
123
  // nobody will change "joined" if we have locked either mutex
124
0
  int result = joined;
125
0
  unlockCerr();
126
0
  if (result) return OFTrue; else return OFFalse;
127
0
}
128
129
OFConsole& OFConsole::instance()
130
15
{
131
15
  static OFConsole instance_;
132
15
  return instance_;
133
15
}
134
135
int OFConsole::old_stderr = -1;
136
137
void OFConsole::mergeStderrStdout()
138
0
{
139
0
    fflush(stderr);
140
0
    if (fileno(stderr) != fileno(stdout))
141
0
    {
142
        /* duplicate the stderr file descriptor to be the same as stdout */
143
0
        if (old_stderr < 0) old_stderr = dup(fileno(stderr));
144
145
        /* now duplicate the file descriptor of stdout into the file descriptor of stderr.
146
         * This will silently close the previous file descriptor of stderr.
147
         */
148
0
        if (0 != dup2(fileno(stdout), fileno(stderr)))
149
0
        {
150
0
            OFConsole::instance().lockCerr() << "Unable to redirect stderr to stdout" << OFendl;
151
0
            OFConsole::instance().unlockCerr();
152
0
        }
153
0
    }
154
155
0
#ifndef HAVE_CLASSIC_BORLAND_COMPILER  /* setvbuf on stdout/stderr does not work with Borland C++ */
156
    /* set stdout and stderr to unbuffered mode */
157
0
    if (setvbuf(stdout, NULL, _IONBF, 0 ) != 0 )
158
0
    {
159
0
        OFConsole::instance().lockCerr() << "Unable to switch stdout to unbuffered mode" << OFendl;
160
0
        OFConsole::instance().unlockCerr();
161
0
    }
162
0
    if (setvbuf(stderr, NULL, _IONBF, 0 ) != 0 )
163
0
    {
164
0
        OFConsole::instance().lockCerr() << "Unable to switch stderr to unbuffered mode" << OFendl;
165
0
        OFConsole::instance().unlockCerr();
166
0
    }
167
0
#endif /* HAVE_CLASSIC_BORLAND_COMPILER */
168
0
}
169
170
171
void OFConsole::unmergeStderrStdout()
172
0
{
173
    /* only execute this code if stderr was actually redirected before */
174
0
    if (old_stderr > 0)
175
0
    {
176
0
        if (0 != dup2(old_stderr, fileno(stderr)))
177
0
        {
178
0
            OFConsole::instance().lockCerr() << "Error: Unable to release redirection of stderr to stdout" << OFendl;
179
0
            OFConsole::instance().unlockCerr();
180
0
        }
181
182
0
#ifndef HAVE_CLASSIC_BORLAND_COMPILER
183
        /* switch stdout to buffered mode */
184
0
        if (setvbuf(stdout, NULL, _IOFBF, BUFSIZ ) != 0 )
185
0
        {
186
0
            OFConsole::instance().lockCerr() << "Error: Unable to switch stdout to buffered mode" << OFendl;
187
0
            OFConsole::instance().unlockCerr();
188
189
0
        }
190
0
#endif /* HAVE_CLASSIC_BORLAND_COMPILER */
191
0
    }
192
0
}
193
194
class OFConsoleInitializer
195
{
196
public:
197
  OFConsoleInitializer()
198
5
  {
199
5
    OFConsole::instance();
200
5
  }
201
};
202
203
/* the constructor of this global object makes sure
204
 * that ofConsole is initialized before main starts.
205
 * Required to make ofConsole thread-safe.
206
 */
207
OFConsoleInitializer ofConsoleInitializer;