-
Notifications
You must be signed in to change notification settings - Fork 1
/
picellif.c
198 lines (166 loc) · 5.03 KB
/
picellif.c
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/* picellif - utility to insert a list of files into the clipboard for subsequent pasting (copying) in Explorer, Outlook, etc.
*
* See LICENSE.txt for copyright and licensing, and README.md for a description.
*
* Usage: picellif filename1.exe file?pattern.* andsoon.txt yougettheidea*.doc
* picellifw is a no-console-creating version
*/
#include <stdio.h> /* printf, etc. */
#include <stdlib.h> /* exit */
#include <wchar.h> /* wcslen */
#include <string.h> /* memcpy */
#include <windows.h> /* lots of things */
#include <shlobj.h> /* DROPFILES */
#include <shellapi.h> /* CommandLineToArgvW */
#include <shlwapi.h> /* Path* */
/* print message and exit
* TODO: handle no-console case better */
static void dieifzero(int expr, char const *strexpr, char const *filename, unsigned lineno);
/* error handling macro; kind of like assert() */
#define DIZ(x) \
dieifzero(!!(x),#x,__FILE__,__LINE__)
/* a DNTL is a double-null terminated list. See http://blogs.msdn.com/b/oldnewthing/archive/2009/10/08/9904646.aspx for more
*
* DROPFILES needs a DNTL, and so it seems simplest to use a DNTL as
* the basic structure for recoding the filelist. An alternative is,
* say, a linked list which one converts to a DNTL when setting the
* DROPFILES instance.
*/
/* append a string to a DNTL. DNTL will be realloc()ed, or, if NULL, malloc()ed */
static void dntl_append(wchar_t **dntl, wchar_t const *s);
/* expand and absolutify a glob pattern, append results to a DNTL.
DNTL will be realloc()ed, or, if NULL, malloc()ed */
static void dntl_append_glob(wchar_t **dntl, wchar_t const *glob);
/* total number of wchars in a DNTL, including terminators */
static size_t dntl_chars(wchar_t const *dntl);
/* copy list to clipboard */
static void dntl_to_clipboard(wchar_t const *dntl);
/* empty clipboard */
static void empty_clipboard(void);
int
main()
{
int argc;
LPWSTR *argv;
wchar_t *dntl= NULL;
int iarg;
DIZ(argv = CommandLineToArgvW(GetCommandLineW(), &argc));
if(argc<2)
{
fprintf(stderr,"Need at least one argument, a filename pattern.\n");
exit(2);
}
for(iarg=1; iarg<argc; iarg++)
{
dntl_append_glob(&dntl,argv[iarg]);
}
LocalFree(argv);
empty_clipboard();
dntl_to_clipboard(dntl);
return 0;
}
static void
dieifzero(int expr, char const *strexpr, char const *filename, unsigned lineno)
{
if(!expr)
{
fprintf(stderr,"Fatal:%s(%u): '%s' evaluated to 0\n",filename,lineno,strexpr);
exit(1);
}
}
static size_t
dntl_chars(wchar_t const *dntl)
{
size_t i=0;
int consecnulls=0;
if(!dntl)
return 0;
while(consecnulls<2)
{
if(dntl[i])
consecnulls=0;
else
consecnulls++;
i++;
}
return i;
}
static void
dntl_append(wchar_t **dntl, wchar_t const *s)
{
size_t s_len= wcslen(s);
size_t dntl_len= dntl_chars(*dntl);
if(!*dntl)
{
DIZ(*dntl=malloc((s_len+2)*sizeof *dntl));
memcpy(*dntl, s, (s_len+1)*sizeof *s);
/* add the second nul terminator */
(*dntl)[s_len+1]=0;
}
else
{
DIZ(*dntl=realloc(*dntl, (dntl_len+s_len+1) * sizeof *dntl));
memcpy((*dntl)+dntl_len-1, s, (s_len+1)*sizeof *s);
(*dntl)[dntl_len+s_len]=0;
}
}
static void
dntl_append_glob(wchar_t **dntl, wchar_t const *glob)
{
WIN32_FIND_DATAW findFileData;
#if defined(_M_IX86)
PVOID fsredir = NULL;
#endif /* defined(_M_IX86) */
wchar_t *filename = PathFindFileNameW(glob);
wchar_t dirname[MAX_PATH];
wchar_t absdirname[MAX_PATH];
HANDLE hFind;
DIZ(filename < glob + MAX_PATH -1);
wcsncpy(dirname,glob,filename-glob);
dirname[filename-glob]=0;
if(!dirname[0]) /* empty string ... */
wcscpy(dirname,L".\\");
GetFullPathNameW(dirname, MAX_PATH, absdirname, NULL);
#if defined(_M_IX86)
DIZ(Wow64DisableWow64FsRedirection(&fsredir));
#endif /* defined(_M_IX86) */
hFind=FindFirstFileW(glob,&findFileData);
/* TODO: actual error message here; user error, not a bug */
DIZ(INVALID_HANDLE_VALUE != hFind );
for(;;)
{
wchar_t abspath[MAX_PATH];
DIZ(PathCombineW(abspath,absdirname,findFileData.cFileName));
dntl_append(dntl, abspath);
if(0==FindNextFileW(hFind,&findFileData))
/* todo: check GetLastError() return */
break;
}
FindClose(hFind);
#if defined(_M_IX86)
DIZ(Wow64RevertWow64FsRedirection(fsredir));
#endif /* defined(_M_IX86) */
}
static void
dntl_to_clipboard(wchar_t const *dntl)
{
size_t dropsize = sizeof(DROPFILES) + dntl_chars(dntl)*sizeof *dntl;
HGLOBAL hGlobal;
DROPFILES *dropfiles;
HANDLE hData;
DIZ(hGlobal = GlobalAlloc(GHND, dropsize));
DIZ(dropfiles = (DROPFILES*) GlobalLock(hGlobal));
dropfiles->pFiles = sizeof *dropfiles;
dropfiles->fWide = TRUE;
memcpy((wchar_t*)(dropfiles+1), dntl, dntl_chars(dntl)*sizeof *dntl);
DIZ(!GlobalUnlock(hGlobal));
DIZ(OpenClipboard(NULL));
DIZ(hData=SetClipboardData(CF_HDROP, hGlobal));
DIZ(CloseClipboard());
}
static void empty_clipboard(void)
{
DIZ(OpenClipboard(NULL));
DIZ(EmptyClipboard());
DIZ(CloseClipboard());
}