Clip: On failure, remember to free the allocated buffer
[AROS.git] / workbench / c / shellcommands / Clip.c
blob78472f917fb8835e73bdc44c163db28ba7d29de3
1 /*
2 Copyright © 2011, The AROS Development Team. All rights reserved.
3 $Id:$
5 Desc:
6 Lang: English
7 */
9 /******************************************************************************
11 NAME
13 Clip
15 TEMPLATE
17 U=UNIT/N/K,W=WAIT/S,G=GET/S,P=PUT=S=SET/S,C=COUNT/S,TEXT
19 LOCATION
23 FUNCTION
25 Handle the clipboard's units (read or write text) from the Shell.
27 FORMAT
29 CLIP [COUNT] [UNIT <unit>] [ GET [WAIT] ] [ SET [TEXT] ]
31 RESULT
33 Standard DOS return codes.
36 ******************************************************************************/
38 #include <datatypes/textclass.h>
40 #include <proto/exec.h>
41 #include <proto/dos.h>
42 #include <proto/iffparse.h>
43 #include <proto/alib.h> // __sprintf()
45 //#define DEBUG 1
46 #include <aros/debug.h>
48 #include <aros/shcommands.h>
51 * otigreat: Without having used the original Clip, it's not clear if
52 * > C:Clip SET
53 * has to set an empty clip unit, or to delete the said clip unit. Though the
54 * latter seems more elegant. Without CLIP_SET_NO_TEXT_MEANS_DELETE_UNIT you
55 * get the other behaviour. The current one, however, also allows to obtain an
56 * empty clip unit by doing:
57 * > C:Clip SET ""
59 #define CLIP_SET_NO_TEXT_MEANS_DELETE_UNIT 1
61 BOOL toClip(STRPTR text, UBYTE clipUnit, struct Library *IFFParseBase);
62 STRPTR fromClip(UBYTE clipUnit, struct Library *IFFParseBase);
64 AROS_SH6H(Clip,50.1, "read from or write to clipboard\n",
65 AROS_SHAH(IPTR *,U= ,UNIT,/N/K,NULL , "Clipboard unit to be used by GET or SET (default = 0)\n"
66 "\t\tUNIT must be between 0 and 255"),
67 AROS_SHAH(BOOL ,W= ,WAIT ,/S,FALSE, "Make GET wait for the UNIT to be filled with text data"),
68 AROS_SHAH(BOOL ,G= ,GET ,/S,FALSE,"\tRetrieve text data from the UNIT"),
69 AROS_SHAH(BOOL ,P=PUT=S=,SET,/S,FALSE, "Store supplied TEXT data in the UNIT"),
70 AROS_SHAH(BOOL ,C= ,COUNT ,/S,FALSE, "Output the number of filled units"),
71 AROS_SHAH(STRPTR, ,TEXT , ,NULL ,"\tText data to be SET in the UNIT, requires to be quoted\n"
72 "\t\tif there is more than a single word\n") )
74 AROS_SHCOMMAND_INIT
76 int rc = RETURN_FAIL;
77 BOOL get = ((SHArg(GET)) || (!SHArg(SET) && !SHArg(COUNT)));
78 BYTE waitSig;
79 UBYTE unit, clipUnitPath[10]; /* "CLIPS:255\0" */
80 ULONG waitMask, sigs;
81 STRPTR outstr = NULL;
82 struct Library *IFFParseBase;
83 struct NotifyRequest *clipUnitNR;
85 if (!SHArg(UNIT))
86 unit = PRIMARY_CLIP;
87 else if ((*SHArg(UNIT) < 0L) || (*SHArg(UNIT) > 255L))
89 PrintFault(ERROR_BAD_NUMBER, (CONST_STRPTR)"Clip");
90 return (RETURN_ERROR);
92 else
93 unit = *SHArg(UNIT);
95 if ((IFFParseBase = OpenLibrary((STRPTR)"iffparse.library", 36)))
97 __sprintf(clipUnitPath, (const UBYTE *)"CLIPS:%d", unit);
98 D(bug("[Clip] clipUnitPath == '%s'\n", clipUnitPath));
100 if(get && SHArg(WAIT))
102 if((waitSig = AllocSignal(-1L)) != -1)
104 if ((clipUnitNR = AllocMem(sizeof(struct NotifyRequest), MEMF_CLEAR)))
106 waitMask = 1L << waitSig;
108 clipUnitNR->nr_Name = clipUnitPath;
109 clipUnitNR->nr_Flags = NRF_SEND_SIGNAL;
110 clipUnitNR->nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
111 clipUnitNR->nr_stuff.nr_Signal.nr_SignalNum = waitSig;
113 StartNotify(clipUnitNR);
114 sigs = Wait(waitMask | SIGBREAKF_CTRL_C);
115 EndNotify(clipUnitNR);
116 FreeMem(clipUnitNR, sizeof(struct NotifyRequest));
118 if (sigs & SIGBREAKF_CTRL_C)
120 SetIoErr(ERROR_BREAK);
121 rc = RETURN_ERROR;
123 else
124 rc = RETURN_OK;
125 } /* if ((clipUnitNR = AllocMem(sizeof(struct NotifyRequest), MEMF_CLEAR))) */
126 FreeSignal(waitSig);
127 } /* if((waitSig = AllocSignal(-1L)) != -1) */
128 else
130 /* :-/ AllocSignal() doesn't SetIoErr() on error... but what is the right error to set? */
131 SetIoErr(ERROR_NO_FREE_STORE);
133 } /* if(SHArg(GET) && SHArg(WAIT)) */
135 /* We don't want to proceed if we had to WAIT and it failed... */
136 if (!(SHArg(WAIT) && (rc != RETURN_OK)))
138 if (get)
140 if ((outstr = fromClip(unit, IFFParseBase)))
142 PutStr(outstr);
143 FreeVec(outstr);
144 FPutC(Output(), '\n');
145 rc = RETURN_OK;
147 else
148 rc = RETURN_ERROR;
151 if (SHArg(SET))
153 #if CLIP_SET_NO_TEXT_MEANS_DELETE_UNIT
154 if (!SHArg(TEXT))
156 if (DeleteFile(clipUnitPath) || (IoErr() == ERROR_OBJECT_NOT_FOUND))
157 rc = RETURN_OK;
159 else
160 #endif
162 if (toClip(SHArg(TEXT), unit, IFFParseBase))
163 rc = RETURN_OK;
164 else
165 rc = RETURN_ERROR;
169 /* There must be a better way??? */
170 if (SHArg(COUNT))
172 UBYTE count = 0;
173 BPTR handle;
174 unit = PRIMARY_CLIP;
177 __sprintf(clipUnitPath, (const UBYTE *)"CLIPS:%d", unit);
178 if ((handle = Open(clipUnitPath, MODE_OLDFILE)) || (IoErr() != ERROR_OBJECT_NOT_FOUND))
180 count++;
181 if (handle)
182 Close(handle);
184 } while (unit++ < 255);
185 Printf((STRPTR)"%d\n", count);
186 rc = RETURN_OK;
188 } /* if (!(SHArg(WAIT) && (rc != RETURN_OK))) */
189 CloseLibrary(IFFParseBase);
190 } /* if ((IFFParseBase = OpenLibrary((STRPTR)"iffparse.library", 36))) */
192 if (rc != RETURN_OK)
193 PrintFault(IoErr(), (CONST_STRPTR)"Clip");
195 return (rc);
197 AROS_SHCOMMAND_EXIT
201 BOOL toClip(STRPTR text, UBYTE clipUnit, struct Library *IFFParseBase)
203 struct IFFHandle *iff;
204 ULONG len;
205 BOOL ok = FALSE;
207 if ((iff = AllocIFF()))
209 if ((iff->iff_Stream = (IPTR)OpenClipboard(clipUnit)))
211 InitIFFasClip(iff);
213 if (!OpenIFF(iff, IFFF_WRITE))
215 if (!PushChunk(iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN))
217 if (!PushChunk(iff, 0, ID_CHRS, IFFSIZE_UNKNOWN))
219 len = text ? strlen((char *)text) : 0; /* strlen() crashes if text == NULL */
221 if (WriteChunkBytes(iff, text, len) == len)
223 ok = TRUE;
225 PopChunk(iff);
226 } /* if (!PushChunk(iff, 0, ID_CHRS, IFFSIZE_UNKNOWN)) */
227 PopChunk(iff);
228 } /* if (!PushChunk(iff, ID_FTXT, ID_FORM, IFFSIZE_UNKNOWN)) */
229 CloseIFF(iff);
230 } /* if (!OpenIFF(iff, IFFF_WRITE)) */
231 CloseClipboard((struct ClipboardHandle*)iff->iff_Stream);
232 } /* if ((iff->iff_Stream = (IPTR)OpenClipboard(clipUnit))) */
233 FreeIFF(iff);
234 } /* if (iff) */
236 return (ok);
240 STRPTR fromClip(UBYTE clipUnit, struct Library *IFFParseBase)
242 struct IFFHandle *iff;
243 struct ContextNode *cn;
244 STRPTR filebuffer = NULL,
245 new_filebuffer = NULL;
246 ULONG filebuffer_size = 0;
247 LONG error;
248 BOOL ok = FALSE;
250 iff = AllocIFF();
251 if (iff)
253 if ((iff->iff_Stream = (IPTR)OpenClipboard(clipUnit)))
255 InitIFFasClip(iff);
257 if (!OpenIFF(iff, IFFF_READ))
259 if (!StopChunk(iff, ID_FTXT, ID_CHRS))
261 for(;;)
263 error = ParseIFF(iff, IFFPARSE_SCAN);
265 if ((error != 0) && (error != IFFERR_EOC))
266 break;
268 if (NULL == (cn = CurrentChunk(iff)))
270 kprintf("[Clip] ZERO CONTEXTNODE!!!\n\n");
271 continue;
274 if ((cn->cn_Type == ID_FTXT) && (cn->cn_ID == ID_CHRS))
276 if (!filebuffer)
278 if (NULL == (filebuffer = AllocVec(cn->cn_Size + 1, MEMF_ANY)))
279 break;
281 ok = TRUE;
283 else
285 if (NULL == (new_filebuffer = AllocVec(filebuffer_size + cn->cn_Size + 1, MEMF_ANY)))
287 ok = FALSE;
288 break;
291 CopyMem(filebuffer, new_filebuffer, filebuffer_size);
292 FreeVec(filebuffer);
293 filebuffer = new_filebuffer;
296 if (ReadChunkBytes(iff, filebuffer + filebuffer_size, cn->cn_Size) != cn->cn_Size)
298 ok = FALSE;
299 break;
302 filebuffer_size += cn->cn_Size;
303 filebuffer[filebuffer_size] = '\0';
304 } /* if ((cn->cn_Type == ID_FTXT) && (cn->cn_ID == ID_CHRS)) */
305 } /* for(;;) */
307 if (filebuffer && !ok) {
308 FreeVec(filebuffer);
309 filebuffer = NULL;
311 } /* if (!StopChunk(iff, ID_FTXT, ID_CHRS)) */
312 CloseIFF(iff);
313 } /* if (!OpenIFF(iff, IFFF_READ)) */
314 CloseClipboard((struct ClipboardHandle*)iff->iff_Stream);
315 } /* if ((iff->iff_Stream = (IPTR)OpenClipboard(clipUnit))) */
316 FreeIFF(iff);
317 } /* if (iff) */
319 return (filebuffer);