Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/tools/install.c
5955 views
1
/*
2
* Simplified implementation of the standard install tool
3
*
4
* Copyright 2025 Alexandre Julliard
5
*
6
* This library is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU Lesser General Public
8
* License as published by the Free Software Foundation; either
9
* version 2.1 of the License, or (at your option) any later version.
10
*
11
* This library is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
* Lesser General Public License for more details.
15
*
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this library; if not, write to the Free Software
18
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
*/
20
21
#include "config.h"
22
23
#include <assert.h>
24
#include <ctype.h>
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <stdarg.h>
28
#include <string.h>
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
32
#include "tools.h"
33
34
/* options */
35
static bool backup;
36
static bool create_symlink;
37
static bool directories;
38
static bool preserve_timestamps;
39
static bool strip;
40
static bool verbose;
41
static const char *group;
42
static const char *mode;
43
static const char *strip_program;
44
static const char *suffix = "~";
45
static const char *target_dir;
46
static const char *user;
47
48
const char *temp_dir = NULL;
49
struct strarray temp_files = { 0 };
50
51
static void fatal_error( const char *msg, ... ) __attribute__ ((__format__ (__printf__, 1, 2)));
52
53
/*******************************************************************
54
* fatal_error
55
*/
56
static void fatal_error( const char *msg, ... )
57
{
58
va_list valist;
59
va_start( valist, msg );
60
fprintf( stderr, "install: error: " );
61
vfprintf( stderr, msg, valist );
62
va_end( valist );
63
exit(1);
64
}
65
66
67
/*******************************************************************
68
* exit_on_signal
69
*/
70
static void exit_on_signal( int sig )
71
{
72
exit( 1 ); /* this will call the atexit functions */
73
}
74
75
76
/*******************************************************************
77
* spawn_cmdline
78
*/
79
static void spawn_cmdline( struct strarray args )
80
{
81
int status;
82
83
if (verbose) strarray_trace( args );
84
if ((status = strarray_spawn( args )))
85
{
86
if (status > 0) printf( "%s failed with status %u\n", args.str[0], status );
87
else fatal_perror( "%s failed", args.str[0] );
88
}
89
}
90
91
92
/*******************************************************************
93
* spawn_program
94
*/
95
static void spawn_program( const char *prog, const char *arg, struct strarray files )
96
{
97
static const unsigned int max_cmdline = 30000; /* to be on the safe side */
98
struct strarray args = empty_strarray, cmd = strarray_fromstring( prog, " " );
99
unsigned int len = 0;
100
101
if (arg) strarray_add( &cmd, arg );
102
103
strarray_addall( &args, cmd );
104
STRARRAY_FOR_EACH( file, &files )
105
{
106
if (len > max_cmdline)
107
{
108
spawn_cmdline( args );
109
args = empty_strarray;
110
strarray_addall( &args, cmd );
111
len = 0;
112
}
113
strarray_add( &args, file );
114
len += strlen( file ) + 1;
115
}
116
spawn_cmdline( args );
117
}
118
119
120
/*******************************************************************
121
* set_file_permissions
122
*/
123
static void set_file_permissions( struct strarray files )
124
{
125
if (user)
126
{
127
const char *chown = getenv( "CHOWNPROG" );
128
spawn_program( chown ? chown : "chown", user, files );
129
}
130
if (group)
131
{
132
const char *chgrp = getenv( "CHGRPPROG" );
133
spawn_program( chgrp ? chgrp : "chgrp", group, files );
134
}
135
if (strip)
136
{
137
if (!strip_program) strip_program = getenv( "STRIPPROG" );
138
spawn_program( strip_program ? strip_program : "strip", NULL, files );
139
}
140
if (mode)
141
{
142
const char *chmod = getenv( "CHMODPROG" );
143
spawn_program( chmod ? chmod : "chmod", mode, files );
144
}
145
}
146
147
148
/*******************************************************************
149
* copy_file
150
*/
151
static void copy_file( const char *src, const char *dst )
152
{
153
int input, output;
154
char buffer[1024 * 1024];
155
ssize_t size, pos, ret;
156
157
if (verbose) printf( "%s -> %s\n", src, dst );
158
159
if ((input = open( src, O_RDONLY | O_BINARY )) == -1)
160
fatal_perror( "%s", src );
161
if ((output = open( dst, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0755 )) == -1)
162
{
163
if (errno == EEXIST) fatal_error( "not overwriting just created %s\n", get_basename(dst) );
164
fatal_perror( "%s", src );
165
}
166
167
while ((size = read( input, buffer, sizeof(buffer) )) > 0)
168
{
169
for (pos = 0; pos < size; pos += ret)
170
{
171
ret = write( output, buffer + pos, size - pos );
172
if (ret <= 0) fatal_perror( "failed to write to %s", dst );
173
}
174
}
175
if (size < 0) fatal_perror( "failed to read %s", src );
176
close( input );
177
close( output );
178
}
179
180
181
/*******************************************************************
182
* rename_file
183
*/
184
static void rename_file( const char *src, const char *dst )
185
{
186
int ret;
187
188
if (backup)
189
{
190
char *backup = strmake( "%s%s", dst, suffix );
191
192
unlink( backup );
193
ret = rename( dst, backup );
194
if (!ret && verbose) printf( "%s -> %s\n", dst, backup );
195
}
196
197
if (verbose) printf( "%s -> %s\n", src, dst );
198
199
unlink( dst );
200
if (rename( src, dst ) == -1) fatal_perror( "failed to create %s", dst );
201
}
202
203
204
/*******************************************************************
205
* install_files
206
*/
207
static void install_files( struct strarray files, const char *dest_dir, const char *dest_file )
208
{
209
struct strarray installed = empty_strarray;
210
211
if (!files.count) fatal_error( "no input file specified\n" );
212
213
mkdir_p( dest_dir );
214
temp_dir = make_temp_dir( dest_dir );
215
216
STRARRAY_FOR_EACH( src_file, &files )
217
{
218
const char *base = get_basename( dest_file ? dest_file : src_file );
219
const char *dest = strmake( "%s/%s", temp_dir, base );
220
221
strarray_add( &temp_files, dest );
222
copy_file( src_file, dest );
223
strarray_add( &installed, dest );
224
}
225
226
set_file_permissions( installed );
227
228
STRARRAY_FOR_EACH( file, &installed )
229
{
230
const char *base = get_basename( file );
231
const char *dest = strmake( "%s/%s", dest_dir, base );
232
233
rename_file( file, dest );
234
}
235
temp_files = empty_strarray;
236
}
237
238
239
/*******************************************************************
240
* install_symlink
241
*/
242
static void install_symlink( const char *dest_dir, const char *src, const char *dst )
243
{
244
#ifndef _WIN32
245
if (!dest_dir) dest_dir = get_dirname( dst );
246
else dst = strmake( "%s/%s", dest_dir, get_basename(dst) );
247
248
mkdir_p( dest_dir );
249
if (verbose) printf( "symlink %s -> %s\n", src, dst );
250
unlink( dst );
251
if (symlink( src, dst )) fatal_perror( "cannot create symlink %s", dst );
252
#else
253
fatal_error( "symlinks are not supported on Windows\n" );
254
#endif
255
}
256
257
258
/*******************************************************************
259
* command-line option handling
260
*/
261
static const char usage_str[] =
262
"Usage: install [OPTIONS] SRC DST\n"
263
" install [OPTIONS] -t DIR [FILES]\n"
264
" install [OPTIONS] -d [DIRS]\n\n";
265
266
enum long_options_values
267
{
268
LONG_OPT_STRIP_PROGRAM = 1,
269
};
270
271
static const char short_options[] = "bcdg:Lm:o:psS:t:v";
272
273
static const struct long_option long_options[] =
274
{
275
{ "strip-program", 1, LONG_OPT_STRIP_PROGRAM },
276
/* aliases for short options */
277
{ "directory", 0, 'd' },
278
{ "group", 1, 'g' },
279
{ "mode", 1, 'm' },
280
{ "owner", 1, 'o' },
281
{ "preserve-timestamps", 0, 'p' },
282
{ "strip", 0, 's' },
283
{ "suffix", 1, 'S' },
284
{ "target-directory", 1, 't' },
285
{ "verbose", 0, 'v' },
286
{ NULL }
287
};
288
289
static void usage( int exit_code )
290
{
291
fprintf( stderr, "%s", usage_str );
292
exit( exit_code );
293
}
294
295
static void option_callback( int optc, char *optarg )
296
{
297
switch (optc)
298
{
299
case LONG_OPT_STRIP_PROGRAM:
300
strip_program = xstrdup( optarg );
301
break;
302
case 'b':
303
backup = true;
304
break;
305
case 'c': /* ignored */
306
break;
307
case 'd':
308
directories = true;
309
break;
310
case 'g':
311
group = xstrdup( optarg );
312
break;
313
case 'L':
314
create_symlink = true;
315
break;
316
case 'm':
317
mode = xstrdup( optarg );
318
break;
319
case 'o':
320
user = xstrdup( optarg );
321
break;
322
case 'p':
323
preserve_timestamps = true;
324
break;
325
case 's':
326
strip = true;
327
break;
328
case 'S':
329
suffix = xstrdup( optarg );
330
break;
331
case 't':
332
target_dir = xstrdup( optarg );
333
break;
334
case 'v':
335
verbose = true;
336
break;
337
case '?':
338
fprintf( stderr, "install: %s\n\n", optarg );
339
usage( 1 );
340
break;
341
}
342
}
343
344
345
/*******************************************************************
346
* main
347
*/
348
int main( int argc, char *argv[] )
349
{
350
const char *dest = NULL;
351
struct strarray files = parse_options( argc, argv, short_options, long_options, 0, option_callback );
352
353
#ifndef _WIN32
354
umask( 022 );
355
#endif
356
357
atexit( remove_temp_files );
358
init_signals( exit_on_signal );
359
360
if (!files.count) fatal_error( "no input file specified\n" );
361
362
if (directories)
363
{
364
STRARRAY_FOR_EACH( file, &files ) mkdir_p( file );
365
return 0;
366
}
367
368
if (create_symlink)
369
{
370
if (files.count != 2) fatal_error( "-L option requires two file arguments\n" );
371
install_symlink( target_dir, files.str[0], files.str[1] );
372
return 0;
373
}
374
375
if (!target_dir) /* compute target dir from destination file */
376
{
377
dest = files.str[--files.count];
378
if (!files.count) fatal_error( "no input file specified\n" );
379
if (files.count > 1) fatal_error( "use -t option to install multiple files\n" );
380
target_dir = get_dirname( dest );
381
}
382
383
install_files( files, target_dir, dest );
384
return 0;
385
}
386
387